Redis有5种基础数据结构,分别为:string(字符串)、list(列表)、hash(字典)、set(集合)、zset(有序集合)。所有数据结构都是key-value形式,通过唯一key来获取对应的value值,不同数据结构的差异在于value结构不同。
string(字符串)
字符串是redis常见的数据结构,内部以字符数组形式表示。字符串是可以修改的字符串,内部结构实现类似于java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配,当字符串小于1MB时,扩容都是加倍分配。如果字符串长度超过1MB,扩容时一次会多扩1MB,字符串最大长度为512MB。
设置值
|
|
- ex seconds:为key设置过期时间(秒)
- px milliseconds:为key设置过期时间(毫秒)
- nx:key必须不存在,才可以执行成功
- xx:与nx相反,key必须存在,才可以执行成功
其中nx和xx也可以使用命令setnx和setex,功能是一样的。但是redis是单线程处理机制,如果多个客户端同时执行setnx key value,只有一个客服端可以执行成功,因此可以作为分布锁的一种实现方案。具体可参考https://redis.io/topics/distlock
获取值
|
|
通过get命令能够获取值定key对应的value值,如果key不存在则返回nil(空)。如果要判断key是否存在也可以通过exists key来判断
批量设置值
|
|
如果需要一次插入多个key-value,则可以通过mset 命令批量设置,用空格分隔,提升插入效率
批量获取值
|
|
mget可以一次获取多个key的值,如果不存在则返回nil,结果是按传入的key顺序返回。批量操作能够提升开发效率,减少多次执行带来的网络时间消耗
计数
|
|
incr命令用于对value做自增操作,返回结果分为三种情况:
- 值不是整数时,返回错误
- 值是整数,返回自增后的结果
- 键不存在,按照值为0进行自增,返回结果为1
除了incr命令,redis还提供了decr(自减)、incrby(自增值定数)、decrby(自减指定数),incrbyfloat(自增浮点数)
追加值
|
|
append可以向字符串尾部追加值,当然也可以通过前面说的set key value xx来更新为新的值
字符串长度
|
|
strlen用于获取字符串长度,如果是中文则占用3个字节
设置指定位置的字符
|
|
用于修改字符串指定位置的字符,offeset从0开始,例如通过setrange name 0 p 修改bost为post
获取部分字符串
|
|
start和end分别是开始和结束的偏移量,offeset从0开始,截取字符串部分字符
内部编码
字符串类型的内部编码包括三种:
- int:8个字节的长整形
- embstr:小于等于39个字节的字符串
- raw:大于39个字节的字符串
redis会根据字符串长度决定使用哪种内部编码实现。可以通过object encoding key获取key的内部编码
|
|
List(列表)
redis中的List相当于java中的LinkedList,它是一种链表而不是数组。List存储了多个字符串,每个自字符串称为元素。车从List中插入(push)和弹出(pop)效率都非常快,时间复杂度为O(1)。当最后一个元素弹出后,list将自动删除回收内存。
List中的元素是有序且可重复的,可以通过索引下标获取指定元素或指定元素范围,下标由0开始,时间复杂度为O(n)。
添加元素
从右侧插入元素
|
|
从左边插入元素
|
|
向某个元素前或者后插入元素
|
|
查找元素
获取指定范围内的元素列表
|
|
获取指定位置的元素
|
|
获取列表长度
|
|
关于索引下标的特点,从左往右分别是0到N-1,从右往左分别是-1到-N。另外范围查询中的end包含了自身
弹出元素
从左侧弹出元素
|
|
从右侧弹出元素
|
|
弹出指定元素
|
|
- count>0,从左到右删除count个元素
- count<0,从右往左删除count绝对值个数的元素
- count=0,删除所有元素
保留指定范围的元素
|
|
修改指定元素
|
|
阻塞操作
|
|
blpop和brpop是lpop和rpop的阻塞版本,timeout指定了阻塞时间,单位为秒。需要注意的是如果列表为空,无元素可弹出,且timeout设置为0,则会持续阻塞,直到列表中有元素为止。
内部编码
List类型内部编码有三种:
- ziplist:当列表元素个数小于list-max-ziplist-entries配置(默认512),同时列表中每个元素的值都小于list-max-ziplist-value配置(64字节),redis会采用ziplist来减少内存使用
- linkedlist:当列表无法满足ziplist的条件时,redis会使用linkedlist作为内部实现
- quicklist:3.2提供的新方式,结合ziplist和linkedlist,将多个ziplist使用双向指针串起来形成quicklist
hash(哈希字典)
hash相当于java中的hashMap,它是无序字典,采用了数组+链表的二维结构,不同的是redis hash的值只能是字符串,并采取渐进式的rehash,在rehash时会保留新旧两个hash结构,查询会同时查询两个hash结构,并且会将旧hash的内容不断迁入新hash,当迁移完成就会由新的hash取而代之。
hash内部存储了很多键值对元素,当hash中最后一个元素被移除,该数据结构自动删除内存回收
设置值
|
|
另外redis提供了hsetnx命令,它们的关系与set和setnx命令一样
获取值
|
|
如果key或者field不存在则返回nil
删除field
|
|
hdel会删除一个或多个field,返回结果为成功删除的field数量,当最后一个field被删除,该数据结构会自动删除
计算field数量
|
|
批量设置或获取
批量设置
|
|
批量获取
|
|
判断field是否存在
|
|
获取所有field
|
|
hkeys是返回hash key中所有的field
获取所有value
|
|
hvals是返回hash key中所有field对应的values
获取所有的field-value
|
|
注意:如果hash中的元素较多,hgetall会存在阻塞redis的可能,如果需要获取所有field-value,可以使用hscan,该命令会渐进式遍历hash
hincrby
|
|
与incrby和incrbyfloat命令一样,作用于field
计算value的长度(3.2)
|
|
内部编码
hash的内部编码有两种:
- ziplist:当hash元素个数小于hash-max-ziplist-entries配置(默认512)、同时所有值小于hash-max-ziplist-value配置(默认64字节),redis会以ziplist作为hash的内部实现
- hashtable:当hash无法满足ziplist的条件时,redis会使用hashtable作为内部实现
set(集合)
set相当于java中的HashSet,也是用于存放多个字符串元素,但它与List的不同之处在于set是无序且唯一的。它的内部实现相当于一个特殊的hash字典,其中所有的value都是NULL。当集合中最后一个元素被移除后,数据结构自动删除回收内存。
Redis集合不仅支持增删改查,还支持多个集合取交集、并集、差集、能解决很多开发中的问题。
添加元素
|
|
删除元素
|
|
计算元素个数
|
|
scard的时间复杂度为O(1),它不会遍历集合所有元素,而是直接调用redis内部的变量
判断元素是否在集合中
|
|
随机从集合返回指定个数的元素
|
|
count是可选参数,默认为1
从集合随机弹出元素
|
|
从3.2开始,spop也支持count参数
获取所有元素
|
|
注意:如果集合元素较多smembers会存在阻塞Redis的可能,可以使用sscan来完成
多个集合的交集
|
|
也可以通过sinterstore destination key [key …]命令将结果保存到一个指定集合中
多个集合的并集
|
|
也可以通过sunionstore destination key [key …]命令将结果保存到一个指定集合中
多个集合的差集
|
|
也可以通过sdiffstore destination key [key …]命令将结果保存到一个指定集合中
内部编码
集合类型的内部编码包括两种:
- intset:当集合中的元素都是整数且个数小于set-max-intset-entries配置(默认512),Redis会采用intset作为集合的内部实现
- hashtable:当集合无法满足intset的条件时,Redis采用hashtable作为集合的内部实现
zset(有序集合)
zset类似于java中SortedSet和HashMap的结合体,一方面它是一个集合,保证了内部value的唯一性,另一方面它给每个value设置一个score(分数)作为value的权重进行排序。其内部实现是由”跳跃列表“的数据结构实现。
添加成员
|
|
Redis 3.2为zadd命令添加了nx,xx,ch,incr四个参数选项
- nx:memeber必须不存在,才可以添加成功
- xx:member必须存在,才可以添加成功,用于更新
- ch:返回zset中member和score发生变化的个数
- incr:对score做增加,相当于zincrby
计算成员个数
|
|
计算成员
|
|
计算成员的排名
|
|
zrank是把score从低到搞排名,zrevrank则相反。
删除成员
|
|
增加成员的score
|
|
查询指定排名范围的成员
|
|
zrange是按分数从低到高返回,zrevrange则相反,withscore能返回成员相应的分数
查询指定分数范围的成员
|
|
zrangebyscore按照分数从低到高返回,zrevrangebyscore则相反。withscores会返回成员相应的分数,limit offset count会限制输出的起始位置和个数
同时min和max还支持开区间(小括号)和闭区间(中括号),-inf和+inf分别代表无限小和无限大,例如
|
|
查询指定分数范围成员个数
|
|
删除指定排名内的升序成员
|
|
删除指定分数范围内的成员
|
|
多个集合的交集
|
|
- destination:将结果保存在到这个key中
- numkeys:key的数量
- key [key …]:具体的key
- weights:每个key的权重,在做交集时,每个key的每个member会将自己的score乘以这个权重,默认为1
- aggregate:计算成员交集后,分值可以利用sum、min、max做汇总,默认时sum
多个集合的并集
|
|
- destination:将结果保存在到这个key中
- numkeys:key的数量
- key [key …]:具体的key
- weights:每个key的权重,在做并集时,每个key的每个member会将自己的score乘以这个权重,默认为1
- aggregate:计算成员交集后,分值可以利用sum、min、max做汇总,默认时sum
内部编码
有序集合类型的内部编码有两种:
- ziplist:当有序集合的成员个数小于zset-max-ziplist-entries配置(默认128),同时每个成员的值都小于zset-max-ziplist-value配置(默认64字节),Redis会以ziplist作为内部实现
- skiplist:当ziplist条件不满足时,有序集合会以skiplist作为内部实现。
Bitmaps
现代计算机采用二进制作为信息的基础单位,1个字节等于8位。Redis提供了Bitmaps来实现对位的操作,Bitmaps可以看作以位为单位的数组,数组每个元素只能存错0或1,数组的下标在Bitmaps叫做偏移量。
设置值
|
|
设置key的第offset个位的值(从0开始计算),插入的value只能是1或者0,在第一次初始化Bitmaps时,如果偏移量较大,那么这个操作将会比价耗时,可能造成redis阻塞
获取值
|
|
获取key的第offset位的值
|
|
获取指定范围内value=1的个数
|
|
计算Bitmaps中第一个值为target的偏移量
Bitmaps的运算
|
|
bitop是一个复合操作,它可以做多个Bitmaps的交集(and)、并集(or)、非(not)、异或(xor)操作并把结果保存在destkey中
HyperLogLog
HyperLogLog并不是一种新的数据结构,而是一种基数算法。通过HyperLogLog可以利用极小的内存空间完成独立总数的统计。
添加
|
|
pfadd用于向HyperLogLog中添加元素,如果成功则返回1
计算独立总数
|
|
pfcoun用于计算一个或多个HyperLogLog的独立总数
合并
|
|
pfmerge可以求出多个HyperLogLog的并集赋值给destkey
HyperLogLog内存占用量非常小,但其存在误差,统计结果不是完全准确。在统计总数时,能够容忍一定的误差时,可以选择HyperLogLog,毕竟其占用的内存非常小。