smembers 会阻塞 redis,仅适用于小集合;大数据量应使用 sscan 分批遍历,注意游标字符串比较、重复遗漏风险及客户端正确处理。

SMEMBERS 返回整个 Set,但大数据量时会阻塞 Redis
直接用 SMEMBERS 拿全量数据最简单,但它是一次性把整个 Set 加载进内存再返回——如果集合有 100 万成员,Redis 就得临时拼一个超大响应包,期间无法处理其他请求。线上服务出现延迟毛刺,十有八九是这个操作惹的祸。
- 只适合小集合(
SCARD - 在主从架构中,大响应还会加重网络和从库解析压力
- 客户端收到的是一次性数组,没分页、没游标,没法“接着上次位置继续拉”
大数据量必须用 SSCAN 替代 SMEMBERS
SSCAN 是唯一可替代的方案,它基于游标分批迭代,不阻塞主线程,还能中断重试。但要注意:它不保证一次遍历拿到全部元素(尤其 Set 在扫描过程中被修改),也不能精确控制每批数量——COUNT 参数只是提示,Redis 可能返回更少甚至空结果。
- 起始游标必须用
0,后续用上一轮返回的游标值 - 每次调用至少要检查返回游标是否为
"0"(字符串),只有这时才算扫完 - 避免设过大的
COUNT(比如10000),实测 > 1000 后吞吐提升有限,反而增加单次耗时 - 示例命令:
SSCAN myset 0 COUNT 100→ 返回游标和一批成员
SSCAN 的重复与遗漏风险怎么应对
Set 是无序结构,SSCAN 的底层实现不提供快照语义。如果遍历中途有人执行 SADD 或 SREM,你可能看到重复元素,也可能漏掉刚删掉的成员——这不是 bug,是设计使然。
- 业务能容忍少量重复/遗漏(如统计近似 UV),就不用额外处理
- 需要强一致性?只能暂停写入(比如维护窗口期),或改用带版本号的外部协调机制
- 别试图靠多次
SSCAN对比取差集来“校准”,成本高且逻辑不可靠 - 注意客户端缓存:某些 SDK 会自动重试游标为
0的情况,确认是否开启该行为
Python 和 Node.js 中的典型误用
很多人写脚本时图省事,把 SSCAN 包进 while 循环却忘了游标更新,或者用错返回格式,结果死循环或漏数据。
- Python redis-py 示例关键点:
cursor, members = r.sscan("myset", cursor=cursor, count=100)—— 必须显式传入cursor,不能依赖默认值 - Node.js ioredis 注意:
client.sscan("myset", cursor, "COUNT", "100")返回的是[cursor, members]数组,不是对象 - 所有语言都要检查
cursor === "0"(字符串比较!不是数字0),否则可能提前退出 - 别在循环里反复创建连接,复用 client 实例
SSCAN 时,最容易忽略的是游标类型判断和重试边界——它返回的游标永远是字符串,哪怕看着像数字;而网络抖动导致某次请求失败时,你得从上一个成功游标继续,不是从头来。










