Redis 源码分析集合对象(z_set)

「这是我参与2022首次更文挑战的第39天,活动详情查看:2022首次更文挑战」。

数据结构

Redis set 对象也是采用了两种方式:intset 和 hashtable 来实现的, hashtable 底层通过 dict 实现。

image-20220220170321750.png

intset 编码

intset 编码的集合对象使用整数集合(intset)作为底层实现,整数集合包含的所有元素都保存在整数集合中。

127.0.0.1:6379> sadd numbers 1 2 3 4 5
(integer) 5
127.0.0.1:6379> object encoding numbers
"intset"
复制代码

hashtable 编码

另外,hashtable 编码的集合对象使用字典(dict)作为底层实现,字典的每个键都是一个字符串对象,每个字符串对象都包含了一个集合元素,而字典的值则全部呗设置为了null。(这块和 jdk 中的 HashSet 实现类似,HashSet 基于 HashMap 实现 val 存的是一个空对象)。

127.0.0.1:6379> sadd numbers "seven"
(integer) 1
127.0.0.1:6379> object encoding numbers
"hashtable"
复制代码

编码转换

满足一下两个条件使用 intset 编码:

  1. 集合对象保存所有的元素都是整数值
  2. 集合对象保存的元素数量不超过 512 个

举个例子(针对第二个条件):

127.0.0.1:6379> eval "for i=1, 512 do redis.call('sadd', KEYS[1], i) end" 1 integers
(nil)
127.0.0.1:6379> scard integers
(integer) 512
127.0.0.1:6379> object encoding integers
"intset"

复制代码

超过 512 个int 类型的值,转换为 hashtable 存储,底层数据集合是 dict

127.0.0.1:6379> sadd integers 8080
(integer) 1
127.0.0.1:6379> scard integers
(integer) 513
127.0.0.1:6379> object encoding integers
"hashtable"
127.0.0.1:6379>

复制代码

注意点

  • 由 intset 转换为 dict 的操作不可逆
  • set 不允许重复的
  • 支持交并补
  • 支持 【set-max-intset-entries】 可在配置文件中进行修改
  • dict 作为底层存储,value 值为 null
  • intset 作为底层对象时,其查找的对象复杂度为 O(logN)

使用场景

  • 标签:主要是用于社交里面感兴趣的内存(注意要保证在一同个事务下完成)

    • 用户添加标签
    sadd user:1:tags tag1 tag2 tag4
    sadd user:2:tags tag1 tag2 tag5
    复制代码
    • 给标签记录用户
    sadd tag1:users user:1 user:3
    sadd tag2:users user:1 user:2
    复制代码
  • 计算用户共同感兴趣的标签

    sinter user:1:tags user:2:tags
    复制代码
  • 抽奖中随机数

    # spop/srandmember
    
    127.0.0.1:6379> sadd userids 1
    (integer) 1
    127.0.0.1:6379> sadd userids 2
    (integer) 1
    127.0.0.1:6379> sadd userids 3
    (integer) 1
    127.0.0.1:6379> srandmember userids
    "2"
    复制代码

常见操作

image.png

参考资料

  1. 《Redis 设计与实现》黄健宏
  2. www.runoob.com/redis/redis…

Guess you like

Origin juejin.im/post/7068306577589010462