0%

Redis数据结构

Redis有5种基础数据结构,分别为:string(字符串)、list(列表)、hash(字典)、set(集合)、zset(有序集合)。所有数据结构都是key-value形式,通过唯一key来获取对应的value值,不同数据结构的差异在于value结构不同。

string(字符串)

字符串是redis常见的数据结构,内部以字符数组形式表示。字符串是可以修改的字符串,内部结构实现类似于java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配,当字符串小于1MB时,扩容都是加倍分配。如果字符串长度超过1MB,扩容时一次会多扩1MB,字符串最大长度为512MB。

设置值

1
set key value [ex seconds] [px milliseconds] [nx | xx]
  • ex seconds:为key设置过期时间(秒)
  • px milliseconds:为key设置过期时间(毫秒)
  • nx:key必须不存在,才可以执行成功
  • xx:与nx相反,key必须存在,才可以执行成功

其中nx和xx也可以使用命令setnx和setex,功能是一样的。但是redis是单线程处理机制,如果多个客户端同时执行setnx key value,只有一个客服端可以执行成功,因此可以作为分布锁的一种实现方案。具体可参考https://redis.io/topics/distlock

获取值

1
get key

通过get命令能够获取值定key对应的value值,如果key不存在则返回nil(空)。如果要判断key是否存在也可以通过exists key来判断

批量设置值

1
mset key value [key value ...]

如果需要一次插入多个key-value,则可以通过mset 命令批量设置,用空格分隔,提升插入效率

批量获取值

1
mget key [key ...]

mget可以一次获取多个key的值,如果不存在则返回nil,结果是按传入的key顺序返回。批量操作能够提升开发效率,减少多次执行带来的网络时间消耗

计数

1
incr key

incr命令用于对value做自增操作,返回结果分为三种情况:

  • 值不是整数时,返回错误
  • 值是整数,返回自增后的结果
  • 键不存在,按照值为0进行自增,返回结果为1

除了incr命令,redis还提供了decr(自减)、incrby(自增值定数)、decrby(自减指定数),incrbyfloat(自增浮点数)

追加值

1
append key value

append可以向字符串尾部追加值,当然也可以通过前面说的set key value xx来更新为新的值

字符串长度

1
strlen key

strlen用于获取字符串长度,如果是中文则占用3个字节

设置指定位置的字符

1
setrange key offeset value

用于修改字符串指定位置的字符,offeset从0开始,例如通过setrange name 0 p 修改bost为post

获取部分字符串

1
getrange key start end

start和end分别是开始和结束的偏移量,offeset从0开始,截取字符串部分字符

内部编码

字符串类型的内部编码包括三种:

  • int:8个字节的长整形
  • embstr:小于等于39个字节的字符串
  • raw:大于39个字节的字符串

redis会根据字符串长度决定使用哪种内部编码实现。可以通过object encoding key获取key的内部编码

1
2
3
4
127.0.0.1:6379> set key 8888
OK
127.0.0.1:6379> object encoding key
"int"

List(列表)

redis中的List相当于java中的LinkedList,它是一种链表而不是数组。List存储了多个字符串,每个自字符串称为元素。车从List中插入(push)和弹出(pop)效率都非常快,时间复杂度为O(1)。当最后一个元素弹出后,list将自动删除回收内存。

List中的元素是有序且可重复的,可以通过索引下标获取指定元素或指定元素范围,下标由0开始,时间复杂度为O(n)。

添加元素

从右侧插入元素

1
rpush key value [value ...]

从左边插入元素

1
lpush key value [value ...]

向某个元素前或者后插入元素

1
linsert key before | after pivot value

查找元素

获取指定范围内的元素列表

1
lrange key start end

获取指定位置的元素

1
lindex key index

获取列表长度

1
llen key

关于索引下标的特点,从左往右分别是0到N-1,从右往左分别是-1到-N。另外范围查询中的end包含了自身

弹出元素

从左侧弹出元素

1
lpop key

从右侧弹出元素

1
rpop key

弹出指定元素

1
lrem key count value
  • count>0,从左到右删除count个元素
  • count<0,从右往左删除count绝对值个数的元素
  • count=0,删除所有元素

保留指定范围的元素

1
ltrim key start end

修改指定元素

1
lset key index newValue

阻塞操作

1
2
blpop key [key ...] timeout
brpop key [key ...] timeout

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中最后一个元素被移除,该数据结构自动删除内存回收

设置值

1
hset key field value

另外redis提供了hsetnx命令,它们的关系与set和setnx命令一样

获取值

1
hget key field

如果key或者field不存在则返回nil

删除field

1
hdel key field [field ...]

hdel会删除一个或多个field,返回结果为成功删除的field数量,当最后一个field被删除,该数据结构会自动删除

计算field数量

1
hlen key

批量设置或获取

批量设置

1
hmset key field value [field value ...]

批量获取

1
hmget key field [field ...]

判断field是否存在

1
hexists key field

获取所有field

1
hkeys key

hkeys是返回hash key中所有的field

获取所有value

1
hvals key

hvals是返回hash key中所有field对应的values

获取所有的field-value

1
hgetall key

注意:如果hash中的元素较多,hgetall会存在阻塞redis的可能,如果需要获取所有field-value,可以使用hscan,该命令会渐进式遍历hash

hincrby

1
2
hincrby key field
hincrbyfloat by field

与incrby和incrbyfloat命令一样,作用于field

计算value的长度(3.2)

1
hstrlen key field

内部编码

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集合不仅支持增删改查,还支持多个集合取交集、并集、差集、能解决很多开发中的问题。

添加元素

1
sadd key element [element ...]

删除元素

1
srem key element [element ...]

计算元素个数

1
scard key

scard的时间复杂度为O(1),它不会遍历集合所有元素,而是直接调用redis内部的变量

判断元素是否在集合中

1
sismember key element

随机从集合返回指定个数的元素

1
srandmember key [count]

count是可选参数,默认为1

从集合随机弹出元素

1
spop key

从3.2开始,spop也支持count参数

获取所有元素

1
smembers key

注意:如果集合元素较多smembers会存在阻塞Redis的可能,可以使用sscan来完成

多个集合的交集

1
sinter key [key ...]

也可以通过sinterstore destination key [key …]命令将结果保存到一个指定集合中

多个集合的并集

1
sunion key [key ...]

也可以通过sunionstore destination key [key …]命令将结果保存到一个指定集合中

多个集合的差集

1
sdiff 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的权重进行排序。其内部实现是由”跳跃列表“的数据结构实现。

添加成员

1
zadd key score member [score member ...]

Redis 3.2为zadd命令添加了nx,xx,ch,incr四个参数选项

  • nx:memeber必须不存在,才可以添加成功
  • xx:member必须存在,才可以添加成功,用于更新
  • ch:返回zset中member和score发生变化的个数
  • incr:对score做增加,相当于zincrby

计算成员个数

1
zcard key

计算成员

1
zscore key memeber

计算成员的排名

1
2
zrank key member
zrevrank key member

zrank是把score从低到搞排名,zrevrank则相反。

删除成员

1
zrem key memeber [memeber ...]

增加成员的score

1
zincrby key increment member

查询指定排名范围的成员

1
2
zrange key start end [withscores]
zrevrange key start end [withscores]

zrange是按分数从低到高返回,zrevrange则相反,withscore能返回成员相应的分数

查询指定分数范围的成员

1
2
zrangebyscore key min max [withscores] [limit offset count]
zrevrangebyscore key max min [withscores] [limit offset count]

zrangebyscore按照分数从低到高返回,zrevrangebyscore则相反。withscores会返回成员相应的分数,limit offset count会限制输出的起始位置和个数

同时min和max还支持开区间(小括号)和闭区间(中括号),-inf和+inf分别代表无限小和无限大,例如

1
127.0.0.1:6379> zrangebyscore user (200 +inf withscore

查询指定分数范围成员个数

1
zcount key min max

删除指定排名内的升序成员

1
zremrangebyrank key start end

删除指定分数范围内的成员

1
zremrangebyscore key min max

多个集合的交集

1
zinterstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
  • destination:将结果保存在到这个key中
  • numkeys:key的数量
  • key [key …]:具体的key
  • weights:每个key的权重,在做交集时,每个key的每个member会将自己的score乘以这个权重,默认为1
  • aggregate:计算成员交集后,分值可以利用sum、min、max做汇总,默认时sum

多个集合的并集

1
zunionstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
  • 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叫做偏移量。

设置值

1
setbit key offset value

设置key的第offset个位的值(从0开始计算),插入的value只能是1或者0,在第一次初始化Bitmaps时,如果偏移量较大,那么这个操作将会比价耗时,可能造成redis阻塞

获取值

1
getbit key offset

获取key的第offset位的值

1
bitcount [start] [end]

获取指定范围内value=1的个数

1
bitpos key target [start] [end]

计算Bitmaps中第一个值为target的偏移量

Bitmaps的运算

1
bitop op destkey key [key ...]

bitop是一个复合操作,它可以做多个Bitmaps的交集(and)、并集(or)、非(not)、异或(xor)操作并把结果保存在destkey中

HyperLogLog

HyperLogLog并不是一种新的数据结构,而是一种基数算法。通过HyperLogLog可以利用极小的内存空间完成独立总数的统计。

添加

1
pfadd key element [element ...]

pfadd用于向HyperLogLog中添加元素,如果成功则返回1

计算独立总数

1
pfcount key [key ...]

pfcoun用于计算一个或多个HyperLogLog的独立总数

合并

1
pfmerge destkey key [key ...]

pfmerge可以求出多个HyperLogLog的并集赋值给destkey

HyperLogLog内存占用量非常小,但其存在误差,统计结果不是完全准确。在统计总数时,能够容忍一定的误差时,可以选择HyperLogLog,毕竟其占用的内存非常小。