964 字
约 3 分钟
8
S12-J05 · 高级用法:延迟队列 / 订阅 / 限流 / SCAN

适用读者:要拿 Redis 干"正经事"的人。
前置:S12-J04。
实测环境:Redis 7.0.15(2026-11-05)。
覆盖旧文:873(90) 871(85) 878(75) 501(36) 2674(95) 1541(25) = 406 点。
覆盖扩展:E-S12-014(延迟队列)、E-S12-015(SCAN 游标)。
上一篇:S12-J04 · 事务、Lua 与 Pipeline
下一篇:S12-A01 · 集群与脑裂。


1. 一句话结论

Redis 的"高级用法"都是用基础结构拼出来的:ZSet 当延迟队列、INCR+EXPIRE 做限流、SCAN 替代 KEYS、pub/sub 做广播(但不保证送达)。


2. 延迟队列:ZSet + score(实测)

ZADD dq <到期时间戳> "job1"
ZRANGEBYSCORE dq -inf <当前时间戳> LIMIT 0 10

实测:

到期前取出: []          ← score 还没到
到期后取出: [job1]      ← 到点后可见
优点 缺点
简单、精确到秒 不保证执行(需消费者轮询)
支持任意延迟 无 ACK/重试(要自己实现)

判据:"消息丢了要紧吗?" 要紧 → 用 MQ(S17);不要紧/量小 → ZSet 够用。


3. 限流:固定窗口与滑动窗口

方案 写法 缺陷
固定窗口 INCR key + EXPIRE 窗口边界可双倍放行
滑动窗口 ZSet 存时间戳 内存占用高
令牌桶 Lua 脚本 实现稍复杂(推荐)

固定窗口的标准写法:

INCR rate:uid:202611051930
EXPIRE rate:uid:202611051930 60

"为什么要 Lua":INCR 与 EXPIRE 要原子(否则 INCR 后崩溃 → key 永不过期)。


4. KEYS vs SCAN(实测)

命令 20 万 key 耗时 阻塞
KEYS * 0.28 s ✅ 全局阻塞
SCAN 0 COUNT 100 0.01 s ❌ 不阻塞

SCAN 的三个保证/不保证:

保证 不保证
遍历期间存在的 key 都会被返回 可能重复返回
不阻塞 不保证"同一时刻快照"

判据:"需要一次性拿到全部 key 吗?" 不需要(能增量处理)→ SCAN;需要 → 换数据结构(如用 Set 维护索引)。


5. 发布订阅的边界(A-871)

特性 pub/sub
送达保证 ❌ 不保证(订阅者掉线就丢)
持久化 ❌
多实例 ❌(Cluster 下需额外处理)

判据:"消息丢了会怎样?" 会出问题 → 用 Stream(5.0+,有消费者组与 ACK)。


6. 统计 1 亿 key 的思路(A-2674)

方案 说明
SCAN + 本地聚合 简单,但慢
HyperLogLog 基数估计(误差 ~0.81%),内存极小
Bitmap 适用于"是否出现过"
抽样 近似统计

判据:"要精确值还是趋势?" 趋势 → HLL/Bitmap;精确 → 别用 Redis 做全量统计。


7. 五个误区

误区 真相
"ZSet 延迟队列可靠" 无 ACK/重试
"INCR+EXPIRE 没问题" 非原子,可能永不淘汰
"SCAN 是快照" 可能重复、不保证一致
"pub/sub 能替代 MQ" 不保证送达
"Redis 能做精确全量统计" 内存与阻塞都不允许

8. 自检

  1. ZSet 延迟队列怎么用?缺陷是什么?
  2. 固定窗口限流的原子性问题?
  3. KEYS 与 SCAN 的实测差距?
  4. pub/sub 与 Stream 的关键差别?
  5. 统计海量 key 的三个方案?

9. 下一篇

S12-A01 · 集群:主从、哨兵、Cluster 与脑裂

用法讲完,进架构师层:复制为什么丢数据、脑裂怎么防。


本篇属于系列《S12 · Redis 与缓存》。旧文来源:A-873/871/878/501/2674/1541;扩展点 E-S12-014/015。延迟队列与 SCAN/KEYS 为 Redis 7.0.15 实测。

S12-J05 · 高级用法:延迟队列 / 订阅 / 限流 / SCAN
https://www.clxhxhhr.top/posts/5354/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。