Redis的Set:你以为我是青铜?其实我是百变星君!

大家好,我是你们的老朋友Redis,今天要给大家讲讲我的秘密武器------Set数据结构。听说有人觉得我就是个青铜选手,只能存存标签、记记好友?今天就让你们见识见识,什么叫"集美貌与才华于一身"的百变星君!


一、Set的基因检测报告(底层编码)

当你们用SADD命令往我身体里塞元素时,我体内会发生奇妙的基因变异。偷偷告诉你们,我有双重人格:

  • intset(整数集合) :当所有元素都是整数且元素数量 ≤512(默认)时,我就是这个精打细算的抠门管家。像这样整整齐齐排排坐:
arduino 复制代码
typedef struct intset {
    uint32_t encoding;  // 比如INTSET_ENC_INT16
    uint32_t length;    // 元素个数
    int8_t contents[];  // 元素数组
} intset;
  • hashtable(哈希表) :当遇到非整数元素,或者元素数量超过阈值,我就秒变土豪,直接甩出哈希表大杀器。这时候每个元素都是哈希表里的key,value统一指向NULL(就是这么任性)。

举个栗子:你往集合里加数字就像往钱包里塞钢镚,用intset省空间;突然要加个"老板的私房钱"这种字符串,钱包瞬间爆炸成保险柜!


二、Set的七十二变(底层数据结构)

1. 整型特工队(intset)

这个编码暗藏玄机:它会根据最大元素自动升级编码方式!比如原本用16位存储数字,突然来了个32768(超过int16),整个集合会立即升级到int32,像变形金刚合体一样酷炫。

ruby 复制代码
127.0.0.1:6379> SADD my_set 1 2 3
(integer) 3
127.0.0.1:6379> OBJECT ENCODING my_set
"intset"
127.0.0.1:6379> SADD my_set 32768
(integer) 1
127.0.0.1:6379> OBJECT ENCODING my_set 
"hashtable"

2. 哈希表狂战士(hashtable)

当元素放飞自我开始乱来时,哈希表就带着O(1)时间复杂度闪亮登场。这时候每个元素都对应哈希表里的一个entry,查找速度堪比闪电侠。


三、Set的职场生存指南(使用场景)

场景1:百万级标签系统(hashtable的高光时刻)

假设你要给10万用户打标签,每个用户有100个标签。用SINTER查共同标签,速度堪比高铁,时间复杂度O(N*M)?不存在的!Redis用了魔法般的算法优化。

bash 复制代码
# 查找喜欢"火锅"和"密室逃脱"的用户
SINTER users:火锅 users:密室逃脱

场景2:实时布隆过滤器(intset的逆袭)

当处理连续数字时,intset的紧凑内存布局堪称内存刺客。比如做ID去重:

yaml 复制代码
# 存储1-10000的连续ID(仅占16KB)
SADD user_ids 1 2 3 ... 10000
# 判断9999是否存在
SISMEMBER user_ids 9999

场景3:社交裂变大师(集合运算)

想要搞"共同好友""你可能认识的人"?Set的并/交/差集操作就是你的瑞士军刀:

bash 复制代码
# 张三的好友 ∩ 李四的好友 = 共同好友
SINTER friends:张三 friends:李四
# 推荐系统:李四好友 - 张三好友 - 张三 = 潜在好友
SDIFF friends:李四 friends:张三 张三

彩蛋场景:老板的紧急需求

"小王啊,咱们要搞个实时在线用户统计,还要能随机踢人!"

"好的老板,我这就用SADD记录登录用户,SPOP随机踢人,SCARD统计在线人数!"


四、Set的灵魂拷问(思考题)

思考题1:intset遇到字符串就要全体叛变?

Q :为什么不能像渣男一样,留几个整数在intset里,只把字符串转成hashtable?
A:这就好比你在用Excel表格记账:

  1. 当你所有数据都是数字时,Excel可以用紧凑的数值格式(intset模式)
  2. 突然混入"老板的私房钱"这样的文本,整个列都会被强制转为文本格式(hashtable模式)

技术真相

  • intset是连续内存块存储,类似数组
  • 插入不同类型元素会导致内存布局崩溃(想象往整型数组里突然插入字符串指针)
  • Redis选择简单粗暴但可靠的方式:要么全数字,要么全对象

灵魂拷问:如果你非要混搭,可以用两个Set(数字Set+字符串Set),但你会收获双倍的快乐(内存开销)!


思考题2:1亿个IP用Set存?老板会杀了你!

Q :假设每个IP占15字节(如"192.168.1.1")
计算器警告

scss 复制代码
哈希表存储成本 ≈ 1亿 * (指针大小8B + Redis对象头16B + IP值15B) ≈ 3.9GB

而用HyperLogLog

复制代码
12KB就能统计去重数量(但无法存储具体IP)

求生指南

✅ 精确去重且需要具体数据 → 用Set但必须分片(每个Set<1万元素)

✅ 允许误差的去重统计 → HyperLogLog

✅ 既要精确又要省内存 → 上Bloom Filter(但Redis需要插件)

血泪教训:曾有人用Set存用户ID,直到服务器内存爆炸才明白------有些爱(数据)注定不能强求!


思考题3:SMEMBERS vs SSCAN,选错会死!

翻车现场

bash 复制代码
# 当Set有1000万元素时
SMEMBERS huge_set # 客户端卡死,Redis内存飙升
SSCAN huge_set 0 COUNT 100 # 每次温柔地取100个

原理暴击

  • SMEMBERS:一次性打包所有元素(O(n)时间复杂度+O(n)网络传输)
  • SSCAN:游标分页获取(O(1)单次操作+可控的数据量)

使用哲学

  • 小数据 → 随便浪(SMEMBERS真香)
  • 大数据 → 要矜持(SSCAN才是真爱)
  • 需要判断存在性 → SISMEMBER永远安全

思考题4:用Set实现分布式锁?骚操作预警!

常规做法(String类型):

bash 复制代码
SET lock_key uuid NX EX 30 # 专业锁匠

邪道玩法(Set类型):

bash 复制代码
SADD global_lock unique_client_id # 利用Set元素唯一性
EXPIRE global_lock 30 # 记得设过期时间!
DEL global_lock # 释放锁时删除自己ID

致命缺陷

  1. 无法续期(像不能充值的停车卡)
  2. 非原子操作(设过期时间可能失败)
  3. 只能实现排他锁(无法升级为读写锁)

生存建议

  • 玩玩可以,生产环境请用Redlock
  • 非要较真的话,建议在辞职信里写上"我用Set实现了分布式锁"

终极总结表

问题 关键点 保命技巧
intset类型转换 内存连续性决定不能混搭 保持元素类型纯净
海量数据存储 哈希表内存放大效应恐怖 分片存储或换用概率型数据结构
遍历大数据 SMEMBERS是煤气灶,SSCAN是电磁炉 永远对数据量保持敬畏
分布式锁 Set能实现幼儿园级别的锁 别拿玩具枪上战场

五、Set的生存法则(最佳实践)

  • 内存敏感型应用:尽量保持元素为整数,控制数量在512以内(可配置)
  • 高频查询场景:多用SISMEMBER代替SMEMBERS+客户端处理
  • 海量数据场景:考虑分片(每个Set不超过1万元素)
  • 精确去重场景:配合HyperLogLog使用效果更佳(但要注意特性差异)

六、最后一道送命题

假设你要设计一个抽奖系统:

  • 总参与人数:50万
  • 每天开奖10次
  • 要防止重复中奖
  • 要实时显示参与人数

你会怎么用Redis Set设计?欢迎在评论区battle,点赞最高的方案送Redis官方马克杯(才怪)!

相关推荐
丘山子20 分钟前
一些鲜为人知的 IP 地址怪异写法
前端·后端·tcp/ip
CopyLower44 分钟前
在 Spring Boot 中实现 WebSockets
spring boot·后端·iphone
天天扭码1 小时前
总所周知,JavaScript中有很多函数定义方式,如何“因地制宜”?(ˉ﹃ˉ)
前端·javascript·面试
.生产的驴2 小时前
SpringBoot 封装统一API返回格式对象 标准化开发 请求封装 统一格式处理
java·数据库·spring boot·后端·spring·eclipse·maven
景天科技苑2 小时前
【Rust】Rust中的枚举与模式匹配,原理解析与应用实战
开发语言·后端·rust·match·enum·枚举与模式匹配·rust枚举与模式匹配
追逐时光者2 小时前
MongoDB从入门到实战之Docker快速安装MongoDB
后端·mongodb
天天扭码3 小时前
深入讲解Javascript中的常用数组操作函数
前端·javascript·面试
方圆想当图灵3 小时前
深入理解 AOP:使用 AspectJ 实现对 Maven 依赖中 Jar 包类的织入
后端·maven
豌豆花下猫3 小时前
Python 潮流周刊#99:如何在生产环境中运行 Python?(摘要)
后端·python·ai
渭雨轻尘_学习计算机ing3 小时前
二叉树的最大宽度计算
算法·面试