参考资料:
redis 4.x cookbook 中文版;
redis官方文档 注: 本文redis的版本为: 5.0.3

Redis 动态字符串扩容 redis字符集_List

数据类型:

目前包括: 字符串类型-string; 列表-list; 散列-hash; 集合-set ;有序集合-sorted set ;HyperLogLog ;Geo;

因为redis中存储的数据都是二进制的
所以如果我们使用jedis这类工具与redis交互时,需要考虑编码的问题;通常在插入redis前与获取数据后统一使用同一种编码;避免不同物理机上可能存在的编码不同的问题;

  • 字符串类型-string

这是最基础也是最简单的数据类型;
在redis中,有三种编码用于存储string:
int存储64位有符号整数;
embstr存储低于44字节的字符串,在内存中使用,性能最高;
raw存储大于44字节的字符串;
redis会根据存储的数据来选择;可以通过OBJECT ENCODING key查看对应value的编码;

#设置值,key已存在,覆盖旧值;
#SET key value [EX seconds] [PX milliseconds] [NX|XX]
#EX(为key设置过期时间,单位秒),PX(为key设置过期时间,单位毫秒);
#NX(不存在才设置值),XX(存在才设置值)
127.0.0.1:6379> set test testValue
OK
#获取值
127.0.0.1:6379> get test
"testValue"
#当key不存在时设置;当key存在时不做任何处理;设置成功返回1;
#这个命令可以用作分布式锁(并发量不高的情况下),需要结合lua脚本,避免线程安全问题;后续再讲;
127.0.0.1:6379> setnx test testValue1
(integer) 0
127.0.0.1:6379> setnx test1 testValue1
(integer) 1
#获取不存在的key,返回nil
127.0.0.1:6379> get test2
(nil)
#获取key对应的value的长度;不存在的key,返回0;
127.0.0.1:6379> strlen test
(integer) 9
127.0.0.1:6379> strlen test2
(integer) 0
#覆盖对应key的值,从指定位置开始;从0开始,下标为4的开始被替换,testValue->test123ue;返回替换后的长度;
127.0.0.1:6379> setrange test 4 123
(integer) 9
127.0.0.1:6379> get test
"test123ue"
#为对应key的value追加字符串;返回追加后的长度;key不存在时,为key设置一个空字符串,再执行append;
127.0.0.1:6379> append test 456
(integer) 12
127.0.0.1:6379> get test
"test123ue456"
#MSET/MGET,是设置与获取的批量操作;MSET无法像SET一样传递参数;
#同理,MSETNX是SETNX的批量操作;
127.0.0.1:6379> MSET test1 testValue_1 test2 testValue_2 test3 testValue_3
OK
127.0.0.1:6379> MGET test1 test2 test3
1) "testValue_1"
2) "testValue_2"
3) "testValue_3"
127.0.0.1:6379>
  • 列表-list

存储一组对象,由于数据结构的原因,它可以用于实现队列[先进先出],栈[先进后出];
redis中的list其实是个双端链表,既可以从开始插入/弹出,也可以从末尾插入/弹出;

#左侧依次插入数据,这里传入多个数据,按顺序插入;在数据库中应是:t3,t2,t1;返回list长度;
#lpushx/rpushx,当key不存在时,不做操作;
127.0.0.1:6379> lpush testList t1 t2 t3
(integer) 3
#查看长度;
127.0.0.1:6379> llen testList
(integer) 3
#从左侧弹出一个值;前面知道t3在最左侧,因此被弹出;被弹出后,list中将会把这个值移除,因此长度为2;
127.0.0.1:6379> lpop testList
"t3"
127.0.0.1:6379> llen testList
(integer) 2
#右侧依次插入数据,数据库中结果应是:t2,t1,t4,t5,t6;返回list长度
127.0.0.1:6379> rpush testList t4 t5 t6
(integer) 5
#右侧弹出值,t6;列表中将其移除;
127.0.0.1:6379> rpop testList
"t6"
127.0.0.1:6379> llen testList
(integer) 4
#从设定的下标开始,到指定的下标结束,获取所有值(注意,这里获取时,包括起始于结束下标位置的值)
#此方法获取值,不会从list中移除对应值;
127.0.0.1:6379> lrange testList 1 2
1) "t1"
2) "t4"
127.0.0.1:6379> lrange testList 0 3
1) "t2"
2) "t1"
3) "t4"
4) "t5"
127.0.0.1:6379> llen testList
(integer) 4
#rpoplpush list1 list2;	将list1最右侧的值弹出,作为list2最左侧的值;list1,list2可以是同一个key;
#返回被弹出和插入的值;
#list2不存在时,将会新建一个list2;
127.0.0.1:6379> rpoplpush testList testList
"t5"
127.0.0.1:6379> lrange testList 0 3
1) "t5"
2) "t2"
3) "t1"
4) "t4"
#lrem key count value,从key中移除count个等于value的值(精确匹配);返回被移除的value个数;
127.0.0.1:6379> lrem testList 5 t
(integer) 0
127.0.0.1:6379> lrange testList 0 3
1) "t5"
2) "t2"
3) "t1"
4) "t4"
127.0.0.1:6379> lrem testList 5 t5
(integer) 1
127.0.0.1:6379> lrange testList 0 3
1) "t2"
2) "t1"
3) "t4"
#根据下标返回对应值;不移除值;
127.0.0.1:6379> lindex testList 0
"t2"
#linsert key before/after targetValue value;
#将value插入到list中目标值的前面/后面;key不存在时不做操作,list中目标值不存在时也不做操作(返回-1);
#操作成功后返回list总长度;
127.0.0.1:6379> linsert testList before 1 t
(integer) -1
127.0.0.1:6379> lrange testList 0 3
1) "t2"
2) "t1"
3) "t4"
127.0.0.1:6379> linsert testList before t1 t
(integer) 4
127.0.0.1:6379> lrange testList 0 3
1) "t2"
2) "t"
3) "t1"
4) "t4"
#将对应下标的值设置为新值;
127.0.0.1:6379> lset testList 1 tn
OK
127.0.0.1:6379> lrange testList 0 4
1) "t2"
2) "tn"
3) "t1"
4) "t4"
#将list从指定开始下标(含)到指定结束下标(含)之间的值保留,其他值都移除
127.0.0.1:6379> ltrim testList 1 2
OK
127.0.0.1:6379> llen testList
(integer) 2
127.0.0.1:6379> lrange testList 0 4
1) "tn"
2) "t1"
#BRPOPLPUSH list1 list2 timeout;rpoplpush list1 list2的阻塞版本;
#timeout设置为0时意味着可以无限阻塞;单位是秒;
#假如在指定时间内没有任何元素被弹出,则返回一个 nil 和等待时长;
127.0.0.1:6379> brpoplpush testList testList 0
"t1"
127.0.0.1:6379> lrange testList 0 4
1) "t1"
2) "tn"
127.0.0.1:6379> brpoplpush testList2 testList2 5
(nil)
(5.08s)
#阻塞的左侧弹出操作,从传入的多个key中,找到第一个list不为空的key,弹出该list的最左侧的值;
#返回被弹出值的key,被弹出的值;
#如果所有key都是空list,则阻塞到设定时间后,返回nil,等待时间;
#BRPOP同理;
127.0.0.1:6379> blpop testList2 testList testList1 3
1) "testList"
2) "t1"
127.0.0.1:6379> lrange testList 0 4
1) "tn"
127.0.0.1:6379> lrange testList1 0 4
1) "t1"
127.0.0.1:6379> blpop testList3 testList4 testList5 3
(nil)
(3.00s)

当多个客户端对同一个key执行阻塞操作时,将会以队列形式处理;先阻塞先执行;

  • 散列-hash

与hashtable类似,redis中的hash类型一样是key-value存储;也有rehash操作;

#设置值;
127.0.0.1:6379> hset testhash field1 value1
(integer) 1
127.0.0.1:6379> hset testhash field2 value2
(integer) 1
#获取值;
127.0.0.1:6379> hget testhash field1
"value1"
#获取所有key-value,大数据量慎用;
127.0.0.1:6379> hgetall testhash
1) "field1"
2) "value1"
3) "field2"
4) "value2"
#与setnx类似,不存在时设置值;
127.0.0.1:6379> hsetnx testhash field3 value3
(integer) 1
127.0.0.1:6379> hsetnx testhash field3 value3-1
(integer) 0
#hash中是否存在field
127.0.0.1:6379> hexists testhash field
(integer) 0
127.0.0.1:6379> hexists testhash field1
(integer) 1
#移除hash中的field
127.0.0.1:6379> hdel testhash field3
(integer) 1
127.0.0.1:6379> hexists testhash field3
(integer) 0
127.0.0.1:6379> hlen testhash
(integer) 2
#获取对应field的value长度;
127.0.0.1:6379> hstrlen testhash field1
(integer) 6
127.0.0.1:6379> hget testhash field1
"value1"
#为某个field的value进行整数运算;最后的参数可以是负数(相当于做减法);
#当value不为数字时,会返回错误;运算成功过后,返回运算后的值;
127.0.0.1:6379> hincrby testhash field1 100
(error) ERR hash value is not an integer
127.0.0.1:6379> hset testhash field4 50
(integer) 1
127.0.0.1:6379> hincrby testhash field4 100
(integer) 150
127.0.0.1:6379> hget testhash field4
"150"
#为某个field的value进行浮点数运算;最后的参数可以是负数(相当于做减法);
#当value不为数字时,会返回错误;运算成功过后,返回运算后的值;
127.0.0.1:6379> hincrbyfloat testhash field1 1.02
(error) ERR hash value is not a float
127.0.0.1:6379> hincrbyfloat testhash field4 1.02
"151.02"
127.0.0.1:6379> hget testhash field4
"151.02"
#批量设置值;
127.0.0.1:6379> hmset testhash field5 value5 field6 value6 field7 value7
OK
#批量获取hash下多个field的值;不存在的field返回nil;
127.0.0.1:6379> hmget testhash field1 field3
1) "value1"
2) (nil)
127.0.0.1:6379> hmget testhash field1 field4 field5
1) "value1"
2) "151.02"
3) "value5"
#获取所有key;由于不支持模糊匹配,所以在生产环境中可以用;
127.0.0.1:6379> hkeys testhash
1) "field1"
2) "field2"
3) "field4"
4) "field5"
5) "field6"
6) "field7"
#获取所有value;由于不支持模糊匹配,所以在生产环境中可以用;
127.0.0.1:6379> hvals testhash
1) "value1"
2) "value2"
3) "151.02"
4) "value5"
5) "value6"
6) "value7"
#hash的扫描;
#hscan key 开始游标(客户端进行第一次遍历时,必须为0) match 正则表达式 count 计数器(期望返回的个数);
#返回下一次遍历开始的游标,当返回0,意味着本次遍历已经结束;
#请注意,最后的count值为3,但是实际上却返回了hash中所有的field;
#这是由于在redis中,当hash的键值对数量少于hash-max-ziplist-entries配置的值时,以ziplist数据类型存储;
#ziplist数据类型是打包后的数据类型,因此不支持遍历,所以返回所有符合条件的数据;
#当键值对大于设置值时,将会转换成hashtable数据结构,使用count字段会起到效果;
#但是,redis不保证每次返回的数据都恰好等于count;
#hscan支持并发访问,因为每个客户端访问都会带来一个游标,同时返回一个新的游标,互相之间不影响;
127.0.0.1:6379> hscan testhash 0 match field* count 3
1) "0"
2)  1) "field1"
    2) "value1"
    3) "field2"
    4) "value2"
    5) "field4"
    6) "151.02"
    7) "field5"
    8) "value5"
    9) "field6"
   10) "value6"
   11) "field7"
   12) "value7"

hash中比较复杂的就是hscan命令了;

  • 集合-set

唯一,无序的对象集合;
通常用来判断对象是否存在,重复删除,并集交集差集等运算;
set-max-intset-entries设置的值,以及集合中元素数据类型可以决定set以何种格式式存储数据;
大于设置值或者集合不全是int,将以hashtable数据结构存储;
小于设置值且集合全是int,以intset格式存储;(此种格式存储速度非常快);

#添加值到集合中,集合不存在则创建新集合;
127.0.0.1:6379> sadd testset value1 value2 value3
(integer) 3
#是否存在指定值;
127.0.0.1:6379> sismember testset value
(integer) 0
127.0.0.1:6379> sismember testset value1
(integer) 1
#移除集合中前面N个值;
127.0.0.1:6379> spop testset 1
1) "value1"
127.0.0.1:6379> sscan testset 0
1) "0"
2) 1) "value2"
   2) "value3"
#随机从集合中获得一个值;
127.0.0.1:6379> srandmember testset 1
1) "value2"
127.0.0.1:6379> srandmember testset 1
1) "value2"
127.0.0.1:6379> srandmember testset 1
1) "value2"
127.0.0.1:6379> srandmember testset 1
1) "value3"
#移除一个值;如不存在返回0;可批量移除,成功后返回移除的个数;
127.0.0.1:6379> srem testset value1
(integer) 0
127.0.0.1:6379> srem testset value2
(integer) 1
127.0.0.1:6379> sscan testset 0
1) "0"
2) 1) "value3"
#证明了set类型是无序集合;
127.0.0.1:6379> sadd testset value4 value5 value6
(integer) 3
127.0.0.1:6379> sscan testset 0
1) "0"
2) 1) "value5"
   2) "value3"
   3) "value6"
   4) "value4"
127.0.0.1:6379> smove testset testset1 value5
(integer) 1
#遍历操作,与hash保持一致;
127.0.0.1:6379> sscan testset1 0
1) "0"
2) 1) "value5"
127.0.0.1:6379> sscan testset 0
1) "0"
2) 1) "value3"
   2) "value6"
   3) "value4"
#结果集大小(对象数量);
127.0.0.1:6379> scard testset
(integer) 3
127.0.0.1:6379> sinter testset testset1
(empty list or set)
#得到集合中所有数据;
127.0.0.1:6379> smembers testset
1) "value3"
2) "value6"
3) "value4"
127.0.0.1:6379> sadd testset value5
(integer) 1
#sinter 两个集合的交集;
#sinterstore 新集合 集合1 集合2;sinter 的结果集写入新集合(如存在则覆盖);返回结果集大小;
127.0.0.1:6379> sinter testset testset1
1) "value5"
127.0.0.1:6379> sinterstore testset3  testset testset1
(integer) 1
127.0.0.1:6379> smembers testset3
1) "value5"
127.0.0.1:6379> sadd testset1 value_1 value_2 value_3 value_4 value_5
(integer) 5
#sunion 两个集合的合集;
#sunionstore 新集合 集合1 集合2;将sunion 的结果集写入新集合(如存在则覆盖);返回结果集大小;
127.0.0.1:6379> sunion testset testset1
1) "value_4"
2) "value_1"
3) "value3"
4) "value_2"
5) "value6"
6) "value4"
7) "value_3"
8) "value5"
9) "value_5"
127.0.0.1:6379> smembers testset
1) "value5"
2) "value3"
3) "value6"
4) "value4"
127.0.0.1:6379> smembers testset1
1) "value_1"
2) "value_4"
3) "value_5"
4) "value_2"
5) "value_3"
6) "value5"
127.0.0.1:6379> smembers testset3
1) "value5"
#sdiff 集合1 集合2;
#以集合1为准,返回的结果集是集合2中没有的数据;如果集合1是集合2的子集,则返回空集合;
#sdiffstore 新集合 集合1 集合2;sdiff 的结果集写入新集合(如存在则覆盖);返回结果集大小;
127.0.0.1:6379> sdiff testset1 testset
1) "value_4"
2) "value_1"
3) "value_5"
4) "value_2"
5) "value_3"
127.0.0.1:6379> sdiff testset3 testset
(empty list or set)
  • 有序集合-sorted set

存储的数据都是根据分数排序好的数据;
当某个值的分数被改变时,如果它与它后面的值具有同样的分数,将会按照原来的顺序排列;
分数可以为负;

#插入值,nx|xx参数与set nx|xx保持一致;
#插入值是,必须是 分数 值 分数 值;且分数必须是数字(可以是浮点数);
127.0.0.1:6379> zadd testrank nx 10 1 20 2 30 3
(integer) 3
#获取有序集合的大小;
127.0.0.1:6379> zcard testrank
(integer) 3
#遍历;
127.0.0.1:6379> zscan testrank 0
1) "0"
2) 1) "1"
   2) "10"
   3) "2"
   4) "20"
   5) "3"
   6) "30"
#获取某个值的权重(分数);
127.0.0.1:6379> zscore testrank 3
"30"
#修改分数;
127.0.0.1:6379> zincrby testrank 10 1
"20"
127.0.0.1:6379> zscan testrank 0
1) "0"
2) 1) "1"
   2) "20"
   3) "2"
   4) "20"
   5) "3"
   6) "30"
127.0.0.1:6379> zincrby testrank 10 1
"30"
127.0.0.1:6379> zscan testrank 0
1) "0"
2) 1) "2"
   2) "20"
   3) "1"
   4) "30"
   5) "3"
   6) "30"
#统计对应分数之间的值个数;
127.0.0.1:6379> zcount testrank 20 20
(integer) 1
127.0.0.1:6379> zincrby testrank 10 1
"40"
127.0.0.1:6379> zcount testrank 20 30
(integer) 2
127.0.0.1:6379> zcount testrank 20 40
(integer) 3
127.0.0.1:6379> zadd testrank -1 4
(integer) 1
127.0.0.1:6379> zscan testrank 0
1) "0"
2) 1) "4"
   2) "-1"
   3) "2"
   4) "20"
   5) "3"
   6) "30"
   7) "1"
   8) "40"
#正序获取值;
127.0.0.1:6379> zrange testrank 0 1
1) "4"
2) "2"
#倒序获取值;
127.0.0.1:6379> zrevrange testrank 0 1
1) "1"
2) "3"
#根据分数,正序获取值;
127.0.0.1:6379> zrangebyscore testrank -1 10
1) "4"
127.0.0.1:6379> zrangebyscore testrank -1 20
1) "4"
2) "2"
#根据分数,倒序获取值;
127.0.0.1:6379> zrevrangebyscore testrank -1 20
(empty list or set)
127.0.0.1:6379> zrevrangebyscore testrank 20 -1
1) "2"
2) "4"
#从集合的正序中,获取某个值的下标;
127.0.0.1:6379> zrank testrank 4
(integer) 0
127.0.0.1:6379> zrank testrank 1
(integer) 3
127.0.0.1:6379> zrank testrank 20
(nil)
127.0.0.1:6379> zrank testrank 2
(integer) 1
127.0.0.1:6379> zscan testrank 0
1) "0"
2) 1) "4"
   2) "-1"
   3) "2"
   4) "20"
   5) "3"
   6) "30"
   7) "1"
   8) "40"
127.0.0.1:6379> zrank testrank 3
(integer) 2
#从集合的倒序中,获取某个值的下标;
127.0.0.1:6379> zrevrank testrank 1
(integer) 0
#移除值,可以是多个;
127.0.0.1:6379> zrem testrank 1
(integer) 1
127.0.0.1:6379> zscan testrank 0
1) "0"
2) 1) "4"
   2) "-1"
   3) "2"
   4) "20"
   5) "3"
   6) "30"   
#根据值的顺序,从有序集合中移除符合条件的值;
127.0.0.1:6379> zremrangebyrank testrank 0 0
(integer) 1
127.0.0.1:6379> zscan testrank 0
1) "0"
2) 1) "2"
   2) "20"
   3) "3"
   4) "30"
127.0.0.1:6379> zremrangebyscore testrank 0 0
(integer) 0
#根据值的权重(分数),从有序集合中移除符合条件的值;
127.0.0.1:6379> zremrangebyscore testrank 18 21
(integer) 1
127.0.0.1:6379> zscan testrank 0
1) "0"
2) 1) "3"
   2) "30"
127.0.0.1:6379> zadd testrank 0 a 0 b 0 c 0 d 0  e
(integer) 5
127.0.0.1:6379> zscan testrank 0
1) "0"
2)  1) "a"
    2) "0"
    3) "b"
    4) "0"
    5) "c"
    6) "0"
    7) "d"
    8) "0"
    9) "e"
   10) "0"
   11) "3"
   12) "30"
#当前有序集合中,所有分数都一样时,根据设定的开闭区间,获取区间内的值;
127.0.0.1:6379> zrangebylex testrank [a (d
(empty list or set)
127.0.0.1:6379> zrem testrank 3
(integer) 1
127.0.0.1:6379> zrangebylex testrank [aaa (e
1) "b"
2) "c"
3) "d"
127.0.0.1:6379> zrangebylex testrank [a (e
1) "a"
2) "b"
3) "c"
4) "d"
#当前有序集合中,所有分数都一样时,根据设定的开闭区间,统计区间内的值个数;
127.0.0.1:6379> zlexcount testrank - +
(integer) 5
127.0.0.1:6379> zlexcount testrank (a (d
(integer) 2
#当前有序集合中,所有分数都一样时,根据设定的开闭区间,删除区间内的值;
127.0.0.1:6379> zremrangebylex testrank [a [c
(integer) 3
127.0.0.1:6379> zscan testrank 0
1) "0"
2) 1) "d"
   2) "0"
   3) "e"
   4) "0"

ZINTERSTORE 新集合 参与计算的集合个数 集合1 … [WEIGHTS 乘数1 …] [AGGREGATE 相同值的分数如何取值在(SUM|MIN|MAX)中选一个]
zunionstore与zinterstore的语法类似;
zunionstore/zinterstore与set类型中的类似都是将并集/交集存储到新的集合中;
注意:
1.参与计算的集合个数必须与集合个数保持一致;
2.集合下标与乘数的下标会对应;(不设置乘数就默认是1)
… 例如:集合1 集合2 集合3 WEIGHTS 乘数1 乘数2 … 在运算的时候,集合1中分数 * 乘数1 集合2中分数 * 乘数2 集合3中分数*1;

  • HyperLogLog

简称HLL(这个实在不好读),通常用来做计数,作用于集合类似,但是它不支持读取内容;

#添加一个元素到一个hll中;成功返回1;
127.0.0.1:6379> pfadd testhll a b c
(integer) 1
#统计这个hll中有多少个元素,得到的就是计数(算上整个系统的延迟,通常是近似值);
127.0.0.1:6379> pfcount testhll
(integer) 3
127.0.0.1:6379> pfadd testhll1 d e f
(integer) 1
#合并两个hll到一个hll;原来的hll还在;返回OK;如果合并后,原hll依然操作,这种情况下,合并后的hll不会变化;
127.0.0.1:6379> pfmerge testhll2 testhll testhll1
OK
127.0.0.1:6379> pfcount testhll2
(integer) 6
127.0.0.1:6379> pfcount testhll
(integer) 3
127.0.0.1:6379> pfcount testhll1
(integer) 3
  • Geo

地图数据类型,里面保存的都是经纬数值,有兴趣的可以去官网看下;