今天给大家简单聊聊 Redis Sorted Set 数据类型底层的实现原理和游戏排行榜实战。特别简单,一点也不深入,也就 7 张图,粉丝可放心食用,哈哈哈哈哈~~~~。
1. 是什么
Sorted Sets 与 Sets 类似,是一种集合类型,集合中不会出现重复的数据(member)。区别在于 Sorted Sets 元素由两部分组成,分别是 member 和 score。
member 会关联一个 double 类型的分数(score),sorted sets 默认会根据这个 score 对 member 进行从小到大的排序,如果 member 关联的分数 score 相同,则按照字符串的字典顺序排序。
这是规则,得记下来。
2-24
常见的使用场景:
-
排行榜,比如维护大型在线游戏中根据分数排名的 Top 10 有序列表。
-
速率限流器,根据排序集合构建滑动窗口速率限制器。
-
延迟队列,score 存储过期时间,从小到大排序,最靠前的就是最先到期的数据。
2. 修炼心法
Sorted Sets 底层有两种方式来存储数据。
-
在 7.0 版本之前是 ziplist,之后被 listpack 代替,使用 listpack 存储的条件是集合元素个数小于等于
zset-max-listpack-entries
配置值(默认 128),且 member 占用字节大小小于zset-max-listpack-value
配置值(默认 64)时使用 listpack 存储,member 和 score 紧凑排列作为 listpack 的一个元素进行存储。 -
不满足上述条件,使用 skiplist + dict(散列表) 组合方式存储,数据会插入 skiplist 的同时也会向 dict(散列表)中插入数据 ,是一种用空间换时间的思路。散列表的 key 存放的是元素的 member,value 存储的是 member 关联的 score。
❝MySQL:“也就是说 listpack 适用于元素个数不多且元素内容不大的场景。”
对,使用 listpack 存储的目的就是节省内存。
Sorted Sets 能支持高效的范围查询,正是因为采用了 skiplist 跳表,比如 ZRANGE
命令时时间复杂度为 O(log(n)) + m
, n 是 member 个数,m 是返回结果数。需要注意的是,你应该避免命令会返回大量结果。
而使用 dict 的原因是实现 O(1) 时间复杂度查询单个元素。比如 ZSCORE key member
指令。
总结来说,Sorted Sets 在插入或者更新的时候,会同时往 skiplist 和 散列表中插入或者更新对应的数据。保证 skiplist 和散列表的数据一致。
❝MySQL:“这个方式很巧妙呀,skiplist 用来根据 score 进行范范围查询或者单个查询,dict 散列表则用于实现 O(1) 时间复杂度查根据数据查询对应 score,满足高效范围查询和单元素查询。“
sorted sets 实现源码主要在以下两个文件中。
-
结构定义在
server.h
。 -
功能实现在
t_zset.c
。
先看 skiplist(跳表&