ZSet数据结构

3 分钟阅读 434 字 + 219 词
Zsets实现原理
zset中的元素由两部分组成,分别是member和score。
zset底层使用两种方式存储数据。
  1. listpack (在7.0版本之前是ziplist):使用条件是集合元素个数小于或等于某个配置值(默认128),且member占用字节数小于或等于zset-max-listpack-value的配置值(默认64)。 将member和score紧凑排列作为listpack的一个元素存储。
  2. skiplist+dict :当不满足上述条件时,将数据分别存储在skiplist和dict中,是一种空间换时间的思想。散列表的key存储的是元素的member,value存储的是member关联的score。
listpack适合 元素个数不多且元素占用空间不大 的场景。
skiplist用来根据score进行范围查询或者单个查询,dict则用于实现以o(1)时间复杂度查询单个元素。
skiplist本质是一种可以 进行二分查找的有序链表 。增加了多级索引,通过索引来实现快速查找。
使用场景:
排行榜,维护游戏中根据分数排名的top10-有序列表。
设置分数为=玩家游戏分+[(基准时间-玩家获得分数时间)/基准时间]
这样在分数相同时,越早获得该分数的排名更前。
速率限流器,根据排序集合构建滑动窗口速率限制器。
# 为每个用户/IP/API创建一个zset
# user:123:requests 是zset的键名
# 当前时间戳是分数
# 请求ID是成员
ZADD user:123:requests <current_timestamp> <request_id>

# 移除时间窗口外的所有请求(例如1分钟前的)
ZREMRANGEBYSCORE user:123:requests 0 <current_timestamp - 60000>

# 计算当前窗口内的请求数
ZCARD user:123:requests

# 如果请求数低于限制,允许请求
# 如果请求数达到或超过限制,拒绝请求
延迟队列,使用score存储过期时间,从小到大排序,最靠前的就是最先到期的数据。