前言
前一篇文章我们深入分析了hash和list两种数据结构,并且列举了它们的使用场景,本篇文章我们将继续研究Redis支持的其他两种数据结构,set和zset。
1.1Set
Set是一个无序集合,它不允许有重复元素。
- 常用命令
命令 | 描述 |
---|---|
sadd key element | 向set添加元素,返回结果为添加成功的元素个数。 |
srem key element | 删除set中元素,返回结果为删除成功的元素个数。 |
scard key | 计算set中元素的个数。 |
sismemeber key element | 判断元素是否在集合中,如果在集合中则返回1,不存在返回0。 |
srandmemeber key [count] | 随机从集合返回指定个数元素,count是可选参数,如果不写默认为1。 |
spop key | 从集合中随机弹出元素。 |
s'members key | 获取集合中所有元素,返回结果是无序的。 |
sinter key [key...] | 求多个集合的交集。 |
suinon key [key...] | 求多个集合的并集。 |
sdiff key [key...] | 求多个集合的差集。 |
- 内部数据结构
Set在Redis中以intset或者hashtable来存储,对于Set,hashtable中的value永远为null。当set中只包含整数型的元素时并且元素个数小于set-max-intset-entries配置(默认512个)时,采用intset实现,否则使用hashtable。
intset是一个字节数组,其中从小到大有序存放着set元素,我们先来看看intset数据结构
typedef struct intset { uint32_t encoding; uint32_t length; int8_t contents[];} intset;
其中encoding和length分别表示每个元素的编码方式和元素个数。redis根据整型位数将intset分为 INTSET_ENC_INT16
、INTSET_ENC_INT32
、INTSET_ENC_INT64
三种编码。
encoding | 类型 | 字节 |
---|---|---|
INTSET_ENC_INT16 | int16_t | 2 |
INTSET_ENC_INT32 | int32_t | 4 |
INTSET_ENC_INT64 | int64_t | 8 |
contents数组用来实际保存数据,数组中元素的特性:无重复元素;元素在数组中递增排列。
进行插入操作时,首先通过二分查找找到本次插入的位置,再对元素进行扩容,再将预计插入位置之后的所有元素向后移动一个位置,最后插入元素。我们用一个流程图来详细描述插入流程,如下图所示
- 使用场景
我们知道服务发现一般有三个角色,服务提供服务消费者和服务中介。服务中介是联系服务提供者和服务消费者的桥梁。服务提供者将自己提供的服务地址注册到服务中介,服务消费者从服务中介那里查找自己想要的服务的地址,然后享受这个服务。服务中介就是一个字典,字典里有很多key/value键值对,key是服务名称,value是服务提供者的地址列表。服务注册就是调用字典的Put方法塞东西,服务查找就是调用字典的Get方法拿东西。当服务提供者节点挂掉时,要求服务能够及时取消注册,比便及时通知消费者重新获取服务地址。当服务提供者新加入时,要求服务中介能及时告知服务消费者,你要不要尝试一下新的服务。
我们用一个set结构存储服务的IP:Port字符串。如果服务提供者加入,调用sadd命令加入服务地址,如果服务挂掉,调用srem命令移除服务地址。对服务消费者使用smembers指令获取所有服务地址然后在消费进程里随机挑一个,或者使用srandmemember指令直接获取随机服务地址。
具体实现方式可以参考
1.2zset
有序集合元素不能重复,并且集合中的元素可以排序。它为每个元素设置一个分数作为排序的依据。
- 常用命令
命令 | 描述 |
---|---|
zadd key score member | 添加成员,返回结果代表成功添加成员个数。 |
zcard key | 计算成员个数。 |
zscore key member | 计算某个成员的分数,如果成员不存在则返回nil。 |
zrank key member | 按分数从低到高返回排名 |
zrevrank key member | 按分数从高到低返回排名 |
zrem key member | 删除成员 |
zincrby key increment member | 增加成员的分数 |
zrange key start end | 返回指定排名范围的成员 |
zrangebyscore key min max | 返回指定分数范围的成员 |
- 内部数据结构
当有序集合的元素个数小于zset-max-ziplist-entries配置时(默认128个),同时每个元素值都小于zset-max-ziplist-value配置时(默认64字节),Redis会采用ziplist来作为有序集合的内部实现。当上述条件不满足时,会采用skiplist作为内部实现。
- 使用场景
有序集合比较典型的使用场景就是排行榜系统,例如视频网站需要对用户上传的视频做排行榜,比如按照获得的赞数进行排序。例如添加用户赞数可以使用:zadd user:ranking:2018_05_23 jack 3。如果此时在获得一个赞,可以使用:zincrby user:ranking:2018_05_23 jack 1。 如果想获得获取赞数最多的是个用户可以使用:zrevrangebyscore user:ranking:2018_05_23 0 9。
1.3总结
今天我们分析了set和zset两种数据结构,下一篇文章我们主要讲一讲持久化的原理。