数据结构
Redis set 对象也是采用了两种方式:intset 和 hashtable 来实现的, hashtable 底层通过 dict 实现。
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 编码:
- 集合对象保存所有的元素都是整数值
- 集合对象保存的元素数量不超过 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"
常见操作
参考资料
- 《Redis 设计与实现》黄健宏