前面在几个文章聊到了 list,string,hash 等结构的实现,这次来聊一下 set 和 sorted set 的细节。
setRedis 的 Set 是 String 类型的无序集合,集合成员是唯一的。
底层实现主要用到了两种数据结构 hashtable 和 inset(整数集合)。
集合中最大的成员数为2的32次方-1 (4294967295, 每个集合可存储40多亿个成员)。
常见命令来看下几个常用的命令
来个栗子
上面重复值的插入,只有第一次可以插入成功
set 的使用场景比较适用于聚合分类
1、标签:比如我们博客网站常常使用到的兴趣标签,把一个个有着相同爱好,关注类似内容的用户利用一个标签把他们进行归并。
2、共同好友功能,共同喜好,或者可以引申到二度好友之类的扩展应用。
3、统计网站的独立IP。利用set集合当中元素不唯一性,可以快速实时统计访问网站的独立IP。
不过对于 set 中的命令要合理的应用,不然很容易造成慢查询
1、使用高效的命令,比如说,如果你需要返回一个 SET 中的所有成员时,不要使用 SMEMBERS 命令,而是要使用 SSCAN 多次迭代返回,避免一次返回大量数据,造成线程阻塞。
2、当你需要执行排序、交集、并集操作时,可以在客户端完成,而不要用SORT、SUNION、SINTER这些命令,以免拖慢 Redis 实例。
看下源码实现这里来看下 set 中主要用到的数据类型
代码路径https://github.com/redis/redis/blob/6.2/src/t_set.c
通过上面的源码分析,可以看到
1、set 中主要用到了 hashtable 和 inset;
2、如果存储的类型是整数类型就会使用 inset,否则使用 hashtable;
3、使用 inset 有一个最大的限制,达到了最大的限制,也是会使用 hashtable;
再来看下 inset 数据结构
代码地址https://github.com/redis/redis/blob/6.2/src/intset.h
insert来看下 intset 的数据插入
intset 的数据插入有一个数据升级的过程,当一个整数被添加到整数集合时,首先需要判断下 新元素的类型和集合中现有元素类型的长短,如果新元素是一个32位的数字,现有集合类型是16位的,那么就需要将整数集合进行升级,然后才能将新的元素加入进来。
这样做的优点:
1、提升整数集合的灵活性,可以随意的添加整数,而不用关心整数的类型;
2、可以尽可能的节约内存。
了解完数据的插入再来看下 intset 中是如何来快速的搜索里面的数据
可以看到这里用到的是二分查找,intset 中的数据本身也就是排好序的
dict来看下 dict 的数据结构
可以看到 dict 中,是预留了两个哈希表,来处理渐进式的 rehash
rehash 细节参考 redis 中的字典
再来看下 dict 数据的插入
这里重点来分析下 Rehash 的过程
1、rehash 的过程 Redis 默认使用了两个全局哈希表;
2、rehash 的过程是渐进式的,因为如果数据量很大的话,一次迁移所有的数据,会造成Redis线程阻塞,无法服务其他请求;
3、在进行 rehash 期间,删除,查找或者更新操作都会在两个哈希表中执行,添加操作就直接添加到哈希表2中了。查找会把两个哈希表都找一遍,直到找到或者两个都找不到;
4、如果在 reash 期间,如果没有读写操作,这时候,就不能迁移工作了,所以后台定时执行一定数量的数据迁移。
sorted setsorted set有序集合和集合一样也是 string 类型元素的集合,同时也不允许有重复的成员。
不同的是sorted set中的每个元素都会关联一个 double 类型的分数,sorted set通过这个分数给集合中的成员进行从小到大的排序。有序集合中的成员是唯一的,关联的 score 可以重复。
常见的命令下面看下有序集合中常见的命令
来个栗子
使用场景来看下sorted set的使用场景
1、通过 score 的排序功能,可以实现类似排行榜,学习成绩的排序功能;
2、也可以实现带权重队列,比如普通消息的 score 为1,重要消息的 score 为2,然后工作线程可以选择按 score 的倒序来获取工作任务。让重要的任务优先执行;
3、也可以实现一个延迟队列,将 score 存储过期时间,从小到大排序,最靠前的就是最先过期的。
分析下源码实现sorted set 中的代码主要在下面的两个文件中
结构定义:server.h
实现:t_zset.c
先来看下sorted set的数据结构
看上面的数据结构可以发现sorted set的实现主要使用了 dict 和 zskiplist 两种数据结构。不过sorted set在元素较少的情况下使用的压缩列表,具体细节参见下文的 zsetAdd 函数。
ZADD来看下 ZADD 的插入
sorted set 的插入使用了两种策略
1、如果插入的数据量和长度没有达到阀值,就使用压缩列表进行保存,反之就使用跳表加哈希表的组合方式进行保存;
2、压缩列表本身是就不适合保存过多的元素,所以达到阀值使用跳表加哈希表的组合方式进行保存;
3、这里跳表加哈希表的组合方式也是很巧妙的,跳表用来进行范围的查询,通过哈希表来实现单个元素权重值的查询,组合的方式提高了查询的效率;
ZRANGE看完了插入函数,这里再来分析下 ZRANGE
通过索引区间返回有序集合指定区间内的成员,因为数据在插入的时候,已经按照从小到大进行了,排序,所以返回指定区间的成员,遍历对应的数据即可。
总结1、Redis 的 Set 是 String 类型的无序集合,集合成员是唯一的;
2、sorted set有序集合和集合一样也是 string 类型元素的集合,同时也不允许有重复的成员。不同的是sorted set中的每个元素都会关联一个 double 类型的分数;
3、set 底层实现主要用到了两种数据结构 hashtable 和 inset(整数集合);
4、sorted set在元素较少的情况下使用的压缩列表存储数据,数据量超过阀值的时候 使用 dict 加 zskiplist 来存储数据;
4、跳表加哈希表的组合方式也是很巧妙的,跳表用来进行范围的查询,通过哈希表来实现单个元素权重值的查询,组合的方式提高了查询的效率。
参考【Redis核心技术与实战】https://time.geekbang.org/column/intro/100056701【Redis设计与实现】https://book.douban.com/subject/25900156/【redis 集合(set)类型的使用和应用场景】https://www.oraclejsq.com/redisjc/040101720.html【跳跃表】https://redisbook.readthedocs.io/en/latest/internal-datastruct/skiplist.html【Redis学习笔记】https://github.com/boilingfrog/Go-POINT/tree/master/redis【Redis 中的 set 和 sorted set 如何使用】https://boilingfrog.github.io/2022/03/21/Redis中的set和sortedset/
到此这篇关于Redis源码分析之set 和 sorted set 使用的文章就介绍到这了,更多相关Redis set 和 sorted set使用内容请搜索七叶笔记以前的文章或继续浏览下面的相关文章希望大家以后多多支持七叶笔记!