Set 经常被用来做标签、去重、关系判断和抽奖池。接口上它只有「无序唯一」这件事,但底层会根据成员形态和规模选择不同编码。

如果成员全是小整数,Redis 可以用 intset 连续存储;如果是较小字符串集合,现代实现还可能用 listpack;当规模或元素长度上来后,最终会进入 hashtable。

先把机制边界说清楚

这一篇关注 Set 的存储编码和集合运算成本。它不是关系数据库,适合快速成员判断,不适合把大规模多集合计算长期压在主线程上。

整体路径

Set 的编码权衡

上面这张图先把主线铺开:整数、小字符串、大集合会走不同存储路径。Set 的成本不仅看成员数量,更要看它落在 intset、listpack 还是 hashtable 编码上,编码决定了内存占用和每次操作的主线程开销。

底层机制

  • intset 用有序整数数组保存成员,内存紧凑,查找用二分 O(logN),但只适合整数成员。触发条件有两个:成员全是整数,且数量不超过 set-max-intset-entries(默认 512)。一旦混入非整数成员或超过 512 个,就会升级成 hashtable(或先经过 listpack)。这个 512 是 Set 编码最经典的阈值,面试高频。
  • listpack 可以承载较小的非整数集合,Redis 7.2+ 给 Set 也设了 listpack 编码(set-max-listpack-entries 128set-max-listpack-value 64),继续换取内存密度。
  • hashtable 用成员作为 key,value 为空,成员判断 O(1) 很快,但对象开销明显增加。
  • intset 内部还有编码升级:如果插入一个更大的整数(比如 int16 集合里插一个超过 int16 范围的值),整个数组的元素都会扩宽到 int32/int64,这是一次整体拷贝。
  • 交并差计算会访问多个集合,大集合之间的运算会长时间占用主线程。

判断一个 Set 编码划不划算,要看成员形态(整数/字符串)、规模(是否过 512)和访问方式(点查还是集合运算)。超过阈值触发编码切换后,内存占用和操作成本都会变化。

取舍与边界

Set 最适合回答「有没有」和「属于哪个小集合」。一旦你开始对百万级集合做交并差,Redis 就从缓存变成了在线计算引擎。

典型问题:用机制化例子排查

  • 关系判断用 SISMEMBER 很合适,批量关系计算要控制集合大小。
  • 避免在线请求里执行大集合 SINTERSUNIONSDIFF,复杂计算尽量离线化。
  • SMEMBERS 对大集合很危险,分页遍历用 SSCAN
  • 排查时看慢日志里的集合命令和 key 的基数,别只看命令复杂度文档。

收束:一句判断

Set 的边界不是唯一性,而是集合规模和计算位置。


关于十三Tech

我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。

我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。

如果你想继续跟完这套「图解 Redis」,欢迎关注公众号 「十三Tech」。后续会继续按数据结构、底层机制、持久化、高可用和实战排查这条线更新。

十三Tech公众号二维码