题记与说明
本文由同日两份章节报告综合而成:纵轴章节(发展脉络)与横轴章节(横向对比),撰写日 2026-10-31。两份章节的全部关键事实均来自当日网络检索与官方文档直接读取(WebSearch / webReader / WebFetch),未依赖纯模型记忆;无法核实出处的厂商宣称与社区报告均已标注口径。两章节之间的事实冲突(如 Valkey 分叉基线版本、Valkey 9.0 特性清单)已在综合时以官方来源为准合并为更精确的表述,并在正文相应处就地注明(见纵轴第五节 Valkey 分叉基线一处)。撰写时 Redis 最新稳定版为 8.10.2(2026-09-17 发布,见 Wikipedia: Redis)。
TL;DR
Redis 的五种经典用法(缓存、队列、锁、限流、发布订阅)不是设计出来的,而是从 2009 年的内存 list 结构里"长"出来的民间 hack。十七年里每种用法都经历同一螺旋:社区拼命令造模式 → 生产事故与理论质疑 → 官方用新原语(Lua、SET NX PX、Streams、Sharded Pub/Sub)收编 → 新共识。2026 年的边界已收敛:缓存与限流是主场;队列是甜点区,且内部分三档:任务队列 List/BullMQ → 要 ACK/回溯/中等保留升 Streams(不离开 Redis)→ 长保留/日亿级/绝对不丢才出走 Kafka/RabbitMQ;锁与发布订阅边界最硬——锁只用于效率型互斥,正确性场景换 etcd/ZooKeeper,Pub/Sub 是 at-most-once,不许丢消息就换 Streams/NATS JetStream/MQTT。判断式一句:先问数据丢失或双主的代价,再问延迟收益,代价高就出 Redis。Valkey 分叉后模式与产品已分离:学模式,绑产品要谈许可。
纵轴:发展脉络——民间 hack、官方收编与理论划界的十七年
"Redis 使用边界"这个课题的实质,是一部民间 hack 如何被官方收编、被理论质疑、再被内建原语修正的历史。五种用法不是先后添加的功能,而是同一套原子命令在不同人手里的不同读法;而"边界"不是写进文档的静态条款,而是一条螺旋线在每一年的切面。
第一阶段(2009–2012):用法先于边界——五个模式的蛮荒起源
2009 年 2 月 26 日,意大利程序员 Salvatore Sanfilippo(网名 antirez)发布 Redis。项目源于他为自家初创公司 LLOOGG 做实时网页访问日志分析:传统数据库撑不住"每秒追加 + 实时聚合"的负载,他先用 Tcl 写概念原型,再用 C 重写,实现的第一个数据类型是 list——也就是说,Redis 生命中第一个"用法"就是今天队列模式的雏形(Wikipedia: Redis)。antirez 把它开源发布到 Hacker News 后迅速在 Ruby 社区走红,GitHub 和 Instagram 是最早的用户。从第一天起,Redis 就同时扮演三个角色:实时数据缓冲区(缓存的近亲)、追加型 list 队列、以及被 antirez 称为 "data structure server" 的新型数据库。
- 队列是最早成熟的用法。2009 年 11 月,GitHub 联合创始人 Chris Wanstrath 发布基于 Redis 的后台任务库 Resque,GitHub 自己就在生产环境使用(Ruby Toolbox: resque)。Resque 的套路是
LPUSH任务 +BRPOP阻塞取任务,确立了此后十年"Redis 做队列"的民间标准做法;2012 年前后更快的 Sidekiq 出现,同样以 Redis 为唯一存储。"Redis 适合当轻量任务队列"作为第一条边界共识,几乎在项目诞生同年就成立了。 - 缓存在 2010 年转向:早期 Redis 内存满时淘汰的是"TTL 最近的键",对纯缓存不友好。2010 年 10 月 antirez 发表《Redis as an LRU cache》,提出基于 LRU 的淘汰——即后来
allkeys-lru/volatile-lru的源头,且实现是抽样近似 LRU 而非精确 LRU(antirez 旧博客)。2011 年 Instagram 工程师 Mike Krieger 公开亿级键值缓存实战(Instagram Engineering),把"Redis 做缓存"从个人技巧变成教科书案例。 - 锁与限流粗糙但已存在:锁是
SETNX+EXPIRE两步写——客户端在两条命令之间崩溃就会留下永不过期的死锁键;限流是INCR+EXPIRE固定窗口。 - 发布订阅在 2010 年的 Redis 2.0 进入主线,antirez 宣布 "Redis is now a Publish/Subscribe server"(redis.io pub/sub 文档)。
本阶段的特征:五种用法全部可用,但边界无人讨论,每种用法都埋着后来爆雷的种子。驱动因素是 NoSQL 浪潮、Rails 生态爆炸与云计算早期对"轻量共享状态"的需求。
第二阶段(2012–2015):官方收编——从民间 hack 到文档模式
转折点是原子性基石。2012 年 10 月 Redis 2.6.0 引入 Lua 脚本与毫秒级过期 PX;2013 年 3 月 2.6.12 让 SET 直接支持 NX/XX/EX/PX——SET lock_key unique_value NX PX 30000 从此成为单命令加锁的标准姿势,埋葬了两步 SETNX+EXPIRE。2013 年的 Redis 2.8 带来 keyspace notifications,"缓存失效通知""过期事件驱动任务"第一次有了官方通道,把 pub/sub 与缓存两条线缝合起来(Keyspace notifications)。
- 锁的第一次官方化(2014):antirez 发表《Distributed locks with Redis》,把民间锁模式正式化——单实例用
SET NX PX+ 随机令牌 + Lua 释放;多实例提出 Redlock:向 N 个独立 master 请求,多数派 + 有效时限内成功才算获得锁(redis.io 分布式锁页面;原始争论见 antirez.com/news/101)。这是第一次有人为"锁"画精确边界。 - 队列的官方自省(2015):antirez 发布独立消息队列实验项目 Disque,动机直白——很多人拿 Redis 当任务队列,但 Redis 本身不是为分布式消息语义设计的(antirez.com/news/88)。创作者亲口承认 list 队列的不足,这是队列边界第一次被官方"自曝"。 2017 年 Disque 被改写为 Redis 模块。
- 限流的算法化(2015):Stripe 工程师 Brandur Leach 系统梳理三类限流模式——固定窗口(
SETEX即可实现,缺点是窗口边界突发)、漏桶(平滑但需要后台 drip 进程)、源自 ATM 网络的 GCRA(无 drip、每键只存一个时间戳,多机部署需对齐时钟)(brandur.org/rate-limiting)。限流从"一个 INCR 命令"升级为有算法谱系的工程课题。
同期背景:2015 年 4 月 Redis 3.0 发布 Cluster(Wikipedia);2015 年 6 月 Redis Labs 接替 Pivotal 赞助 Redis(antirez.com/news/81)。生产事故倒逼模式文档化,antirez 亲自下场写"官方模式"页面。
第三阶段(2016–2018):理论清算——边界第一次被系统性质疑
世纪之争:Kleppmann vs antirez(2016)。 2016 年 2 月 8 日,《DDIA》作者 Martin Kleppmann 发表《How to do distributed locking》公开炮轰 Redlock:它既不是"效率锁"(出错多等一下无妨)也不是"正确锁"(用于守护正确性);依赖各节点时钟的假设在异步系统里站不住;GC 停顿或时钟跳变会让旧持有者继续写入脏数据;而 Redlock 无法生成单调递增的 fencing token,无法让存储层拒绝过期持有者的写入——正确性场景应改用 ZooKeeper/etcd(martin.kleppmann.com)。次日 antirez 回击《Is Redlock safe?》逐点反驳(antirez.com/news/101)。争论没有裁判宣判,但深刻改变了行业认知——"锁分两种:只求效率的可以用 Redis,关乎正确性的请用专用协调服务"自此成为社区默认边界,这也是本课题史上最重要的一次边界划定。
修成正果:Streams(2018)。 2018 年 10 月 Redis 5.0 引入十年间公认的最重要的全新核心数据类型 Streams:append-only 日志 + 消费组 + XACK 确认 + pending entries 崩溃恢复(Redis 5.0 is here!)。官方明确将 Streams 定位为对"用 List 或 Pub/Sub 凑合"的替代:list 队列没有消费组、不能回溯;pub/sub 是 fire-and-forget,掉线的订阅者直接丢消息。队列和发布订阅两条线的边界至此重画:要求不丢消息的用 Streams,允许丢失、只要低延迟广播的才用 pub/sub。
缓存与限流同步深耕:2017 年 Redis 4.0 GA 带来 LFU 淘汰策略(承认"频率比新近度更符合缓存规律")、Modules 系统与 LAZYFREE(Hacker News 讨论);2018 年 Brandur 把 GCRA 做成 Rust 编写的 Redis 模块 redis-cell,一条 CL.THROTTLE 原子完成限流(brandur/redis-cell)。驱动因素:DDIA 出版与 Jepsen 测试让分布式理论大众化;云托管 Redis 让正确性焦虑上升;云厂商白嫖开源引发许可警觉——2018 年 8 月 Redis 模块改用 Apache 2.0 + Commons Clause,2019 年 2 月换成 RSALv2(Wikipedia)。
第四阶段(2019–2023):平台化时代——官方接管边界叙事
2020 年 4 月 Redis 6.0 GA,antirez 称其为"史上最大版本":ACL、RESP3、多线程 I/O,以及客户端缓存——数据变更时服务端主动推送失效消息,让客户端本地缓存保持一致(redis.io blog: Redis 6.0 GA;antirez.com/news/130)。当"缓存之王"开始帮你在客户端再做一层缓存,说明缓存场景已卷到极致。同年 6 月 30 日 antirez 发表《The end of the Redis adventure》正式退位(antirez.com/news/133;The Register 报道)——个人英雄时代结束,边界叙事从此由公司接管。
2022 年 4 月 Redis 7.0 GA:Functions 取代手管 Lua 脚本,让限流/锁等原子模式可随库部署、版本化管理;对本课题更重要的是 Sharded Pub/Sub——普通 pub/sub 在集群模式下要向所有节点广播,扩展性差;分片 pub/sub(SSUBSCRIBE/SPUBLISH)按 key 的 slot 分配 channel,消息只在本分片传播(redis.io pub/sub 文档)。pub/sub 的"大集群广播风暴"边界问题被官方原语正面修复。 驱动因素:企业级用户(银行、电商)把 Redis 用在远超最初设想的位置,官方被迫把每种用法都做成"可以放心买"的企业功能。
第五阶段(2024–2026):分叉与回归——边界之外的战争
- 许可证地震与 Valkey 分叉(2024):2024 年 3 月,Redis 宣布从 7.4 版本起核心代码改用 RSALv2/SSPLv1 双许可——均非 OSI 开源许可;Linux Foundation 随即以最后一个 BSD 版本(7.2.4;两章节此处口径不一,综合时取 7.2.4)为基础发起 Valkey 分叉(BSD-3-Clause),AWS、Google Cloud、Oracle 等为创始赞助方(Wikipedia;valkey-io/valkey;Phoronix 报道)。
- antirez 回归与 Redis 8 的开源重启(2024–2025):2024 年 11–12 月,离开约 1620 天的 antirez 重返 Redis(redis.io blog: Welcome back)。2025 年 5 月 1 日 Redis 8.0 GA,重新以 AGPLv3 提供三重许可(RSAL/SSPL 可选),把 JSON、时间序列、概率结构、Query Engine 向量检索与 Vector Sets 整体并入开源核心,官方宣称对比 7.x 最高 87% 延迟改善(antirez.com/news/151;redis.io blog: AGPLv3;Redis 8 GA)。Redis 的故事重心从"五种经典用法"扩展到"AI 时代的向量与检索",经典边界反而沉淀为稳定地基。
- 两条线并行推进(2025–2026):Redis 线——8.2(2025-08-08 GA,命令提速 35%、吞吐 +49%)、8.4(2025-11-25 GA,混合检索、更聪明的 Stream 处理)、8.6(2026-02 GA,Streams 安全护栏、热点键可见性)、8.8 直至当前 8.10.2(Redis 8.2 GA;8.4 GA)。社区还在提议把 GCRA 做成原生限流命令(Redis 仓库 issue #17274,为提案而非已发布特性)。Valkey 线——分叉后半年即发 8.0,2025-04 发 8.1;2025-10-21 的 9.0 带来 hash 字段级过期、原子 slot 迁移与集群模式多 DB,官方宣称十亿级 RPS 集群、吞吐较上代提升约 40%(Linux Foundation 新闻稿);9.1(2026-05)继续削减每键内存开销(Linux Foundation: A Year of Valkey)。
第六阶段:2026 年的边界共识
十七年轮回后,五种用法的边界已基本收敛(对上述史实的归纳,属社区共识性判断):
| 用法 | 适合 | 不适合 / 边界红线 |
|---|---|---|
| 缓存 | 读多写少、可容忍回源、需要 LRU/LFU 淘汰与 TTL;6.0 起还能下推到客户端缓存 | 把唯一数据副本放进来;要求强一致的读 |
| 队列 | 轻量任务队列(List/BRPOP 或 Resque/Sidekiq/BullMQ 类库);需要消费组、ACK、回溯时用 Streams(5.0+) | 海量堆积、严格事务/顺序/幂等语义、审计级不丢消息——交给 Kafka/RabbitMQ |
| 锁 | 效率型互斥(防止重复工作):SET NX PX + 随机令牌 + Lua 释放 | 正确性关键路径(资金、库存扣减):时钟假设与无 fencing token 的缺陷意味着应选 ZooKeeper/etcd |
| 限流 | 固定窗口(INCR+EXPIRE)、滑动窗口(ZSET)、GCRA(redis-cell 或手写 Lua) | 跨大集群的全球精确配额、计费级精确性——需中心化账本或专用限流服务 |
| 发布订阅 | 低延迟、可容忍丢失的实时广播;大集群用 7.0 sharded pub/sub | 任何"不能丢"的消息——pub/sub 是 fire-and-forget,掉线即丢,须改用 Streams 或 MQ |
贯穿全程的规律:Redis 的每一次边界修正都遵循同一条路径——社区 hack → 生产事故/理论质疑 → 官方原语收编 → 新共识。list 队列催生 Streams,SETNX 竞态催生 Lua 与 SET NX PX,pub/sub 丢消息催生 Streams 与 sharded pub/sub,Redlock 之争催生"效率锁/正确性锁"二分法,INCR 限流的粗糙催生 redis-cell 与原生 GCRA 提案。
横轴:横向对比——2026-10-31 时间截面上的生态位
(许可风波与版本史已在纵轴交代,本节只把它作为选型前提引用,不再重复时间线。)
研究对象与竞品的三类划分
Redis 在 2026 年的官方定位早已不是"缓存",而是"实时数据平台":内存数据结构服务器,附带 JSON、时间序列、概率结构、向量检索等 Stack 级能力。因此它的"竞品"必须拆成三类,单一对比表会把问题搅浑:
| 类别 | 成员 | 竞争关系本质 |
|---|---|---|
| A. 同构竞品(内存 KV) | Valkey、Memcached、Dragonfly、KeyDB、Microsoft Garnet | 抢同一份"内存 KV"工作负载 |
| B. 异构替代(各场景的专业工具) | 队列:Kafka、RabbitMQ、NATS JetStream、云 SQS;锁:etcd、ZooKeeper、数据库约束;限流:nginx limit_req、Envoy RLS、云 API 网关;发布订阅:NATS、MQTT、Kafka | 在单一场景上用"专门的正确性/规模"换 Redis 的"顺手" |
| C. 托管形态 | AWS ElastiCache、GCP Memorystore、Redis Cloud、Upstash | 决定"谁替你运维它"以及计费模型 |
当下格局:许可证风波尘埃落定后的三个阵营
- 分叉已成事实:Valkey 因 BSD 宽松许可被云厂商接纳,且无法回灌 Redis 8+ 的代码,两条路线当前已分叉、尚无合并迹象;对许可敏感的企业(尤其云上自管),Valkey 已是默认选项。
- 同构竞品洗牌:KeyDB 实质停更(最后发版停在 2023 年,社区公开建议迁往 Valkey);Microsoft Garnet(2024 年开源,C#,RESP 协议兼容)在高连接数下吞吐占优,但不支持 Redis 模块,定位是缓存存储而非完整替代;2025 年 10 月的系统论文(arXiv 2510.19805)结论也是:迁移场景优先推荐 Valkey,Garnet 仅在高吞吐 + 不依赖模块时值得考虑。
- Dragonfly 继续以"多线程 Redis 替代"为卖点:thread-per-core、shared-nothing,宣称 25 倍 QPS 与 30% 内存节省(dragonflydb.io)。注意这是厂商自测口径——目前可查的独立实测主要是一份对比 Redis 8.4.0 的 Reddit 社区基准:复现出的领先幅度在 1.2~3 倍量级,其中 Dragonfly 小对象写入仅领先约 9%(Reddit 社区基准帖)。单一样本不足以断言"普遍如此",但与厂商宣称的数量级差距至少值得警惕。
小结:Redis 不再独占"开源内存 KV"心智,但依然独占"功能全 + 生态最厚"的位置。Valkey 是协议级平替(尤其对云厂商客户),Memcached 是极简缓存的守护者,Dragonfly/Garnet 是性能特化选项。
总对比表(2026-10-31 时间截面)
| 维度 | Redis 8.x | Valkey 9.x | Memcached | Dragonfly | Kafka | RabbitMQ | etcd/ZooKeeper | NATS Core/JetStream | MQTT Broker |
|---|---|---|---|---|---|---|---|---|---|
| 定位 | 多面手数据结构服务器 | Redis 协议平替 | 纯缓存 | 多线程内存 KV | 分布式事件日志 | 消息代理 | 共识协调/锁 | 轻量 pub/sub(可选持久) | IoT 消息总线 |
| 许可 | AGPLv3/RSAL/SSPL 三选一 | BSD-3 | BSD-3 | BSL→Apache-2.0(已转) | Apache-2.0 | MPL-2.0 | Apache-2.0 | Apache-2.0 | 各家不一 |
| 持久化 | RDB/AOF 可选 | 同 Redis | 无 | 可选快照/AOF | 强(log) | 强 | 强(WAL+快照) | Core 无 / JetStream 有 | 视 broker |
| 投递语义 | Pub/Sub 至多一次;Streams 至少一次 | 同 Redis | — | — | 至少一次(可幂等近精确一次) | 可靠投递+复杂路由 | 串行化保证(CP) | Core 至多一次 / JetStream 至少一次 | QoS 0/1/2 可选 |
| 运维重量 | 低~中 | 低~中 | 极低 | 低~中 | 高 | 中 | 中~高(3/5 节点共识) | 低(单二进制) | 低~中 |
| 生态/客户端 | 最厚 | 直接复用 Redis 客户端 | 极厚但极简 | 复用 Redis/Memcached 客户端 | 最厚(流生态) | 厚 | 成熟 | 增长快 | IoT 域成熟 |
| 一句话边界 | 什么都能干、强保证要掂量 | 要 BSD/云厂商支持时的 Redis | 只做缓存时更快更省 | 大核多线程时换它 | 不丢、要回放、海量扇出 | 复杂路由+任务队列 | 锁的正确性兜底 | Redis Pub/Sub 的升级位 | IoT/离线设备 |
五大场景逐项对比
缓存:Redis vs Memcached vs 本地缓存 vs Dragonfly
| 维度 | Redis | Memcached | 本地缓存(Caffeine/Guava) | Dragonfly |
|---|---|---|---|---|
| 数据类型 | String/Hash/Set/ZSet/Stream 等丰富结构 | 仅字符串 | 对象(进程内) | 兼容 Redis 常用命令 |
| 线程模型 | 命令执行单线程(6+ 有 I/O 线程) | 原生多线程 | 进程内零网络 | thread-per-core |
| 持久化/复制 | 有 | 无 | 无(进程重启即失) | 有(可选) |
| 适合 | 结构化缓存、排行榜、会话、计数 | 小 value、超高读吞吐、不求持久 | 亚微秒级热点读 | 大核机器上的超大规模缓存 |
要点:Memcached 在"小而简单、纯读放大"的负载上吞吐强、行为可预期,AWS 官方对比页明确把"简单 KV 缓存"留给它(AWS: Redis vs Memcached);但缓存里一旦出现 ZSet 排行榜、Hash 会话、原子计数、击穿保护(SET NX 互斥重建),Memcached 无力承担——这是 Redis 缓存份额远超 Memcached 的主因。本地缓存不是竞品是互补:Redis 自身网络往返(约 0.2~1ms)就是"每请求都打 Redis"架构的新瓶颈,业界标准做法是进程内 L1 + Redis L2 多级缓存——这是 Redis 的物理边界。Valkey 在缓存场景与 Redis 基本无差别,选它通常是商业/许可决定而非技术决定。
场景结论:缓存是 Redis 最安全的主场,但"纯字符串 + 极高吞吐"可换 Memcached,"百万级 QPS 少节点"可试 Dragonfly,"每请求都要读缓存"必须加本地 L1。
本文不展开的相邻课题:缓存与数据库的一致性(cache-aside 回源、延迟双删、基于 binlog 的 CDC 失效同步)以及穿透/雪崩/击穿的成套防护(空值缓存、布隆过滤器、TTL 随机化;击穿互斥仅在本文缓存要点与速查表中带过)各自都是独立课题,边界结论另需专门研究,本文只回答"缓存这个角色该不该交给 Redis"。方向上一句话:默认 cache-aside + TTL,强一致的失效同步走 binlog/CDC 通道——6.0 的 client-side caching 与 2.8 的 keyspace notifications 是官方为这类方案预留的钩子(见纵轴第二、四阶段),但完整方案仍需应用层自行设计。
队列:Redis(Lists/Streams/BullMQ)vs Kafka vs RabbitMQ vs NATS JetStream
| 维度 | Redis Lists | Redis Streams | BullMQ/Sidekiq | Kafka | RabbitMQ | NATS JetStream |
|---|---|---|---|---|---|---|
| 抽象 | 简单阻塞队列 | 日志 + 消费组 | 应用层作业队列 | 分区日志 | 交换机/队列路由 | 流式 pub/sub |
| ACK/重试 | 无(BRPOP 取走即丢) | XACK + XAUTOCLAIM | 内置重试/退避/死信 | offset 提交 | per-message ack | 消费者 ack |
| 回放 | 不可能 | 可以(按 ID + XTRIM) | 不适用 | 核心能力 | 不支持 | 支持 |
| 堆积容忍 | 受内存限制 | 受内存限制(需 XTRIM) | 依赖 Redis 内存 | 磁盘,TB 级常态 | 磁盘 | 文件流 |
| 延迟 | 最低(亚毫秒) | 极低 | 低 | 十毫秒级典型 | 毫秒级 | 极低 |
| 运维 | 已有 Redis 即零增量 | 同左 | 同左 | 重 | 中 | 轻 |
要点:Lists 是陷阱区——LPUSH+BRPOP 没有任何确认机制,worker 崩溃即丢任务,只适合"丢了无所谓"的轻量任务,这是最常见的 Redis 越界使用,而修复的第一站是升 Streams,不是直接换 MQ。作业队列(发邮件、转码、回调重试)是甜点区:BullMQ(bullmq.io)与 Sidekiq/Resque 用 Redis 实现了重试、退避、优先级、限速、仪表盘。官方文档对 Streams 的推荐场景就是"需要更强投递保证时从 Pub/Sub 升级",同时诚实承认其与 Kafka 的差距在"海量扇出 + 长保留 + 严格有序分区"(Redis Streams 教程)。至此,Redis 队列的升级路径是清晰的三档阶梯:List/BullMQ 甜点区 → Streams(要 ACK/回溯/中等保留,不离开 Redis)→ Kafka/RabbitMQ/NATS JetStream(长保留/大消息/绝对不丢/日亿级)。什么时候必须离开 Redis(第三档):消息需要天级以上的长保留回放(Streams 能按 ID 回放,但数据在内存里,XTRIM 掉即不可回溯,撑不住长保留)、单一主题每天数亿条以上、消费者要与主题解耦扩容、消息体积大(≈1MB 级)、或"绝对不能因 Redis OOM 丢事件"——满足任一条,选 Kafka(记录)或 RabbitMQ(路由),NATS JetStream 是"比 Kafka 轻、比 Redis Pub/Sub 稳"的中间带。一条社区经验法则:"要路由用 RabbitMQ,要大规模记录用 Kafka,中等规模又不想加运维用 Redis Streams。"
场景结论:Redis 适合"低价值快任务 + 已有 Redis + 中等规模"三条件同时成立;任一条件松动,先升 Streams/BullMQ 这一档——要 ACK、回溯、中等保留,仍在 Redis 内;只有长保留回放、日亿级、大消息、绝对不丢这类第三档信号出现,才升级到 Streams 之外的专门 MQ。
锁:Redis SET NX vs Redlock vs etcd/ZooKeeper vs 数据库
| 维度 | 单实例 SET NX PX + Lua 解锁 | Redlock(多节点过半) | etcd / ZooKeeper | 数据库唯一约束/事务 |
|---|---|---|---|---|
| 定位 | 效率型互斥 | 宣称的容错互斥 | 正确性型协调(CP) | 以数据为中心的防重 |
| 共识 | 无 | 5 独立节点多数派 | Raft/ZAB 共识 | 存储引擎事务 |
| fencing token | 无 | 无 | 有(zxid/revision 单调递增) | 版本号/自增 id |
| 典型故障翻车 | 主从切换后锁丢失 | 长 GC 停顿/时钟跳变后双持锁 | 罕见,需正确处理会话租约 | 依赖正确建模 |
| 成本 | 一条命令 | 5 个独立实例 | 3~5 节点共识集群 | 零新增组件 |
要点:2016 年 Kleppmann vs antirez 之争(纵轴第三节)到今天仍是权威边界,其结论是二分法——锁为了效率(偶尔失效只是多做一遍工作),单实例 Redis 锁 + 过期时间足够,Redlock 是多余的复杂度;锁为了正确性(失效即数据损坏),则不要用 Redlock,用共识系统并配合 fencing token(Kleppmann 原文;antirez 回应)。十年下来工业界实操共识更接近 Kleppmann 的版本——这是本文对上述材料与行业实践的归纳判断,争论本身并无权威裁决。fencing token 是锁问题的真正解药:Redlock 的随机 value 不携带单调顺序,GC 停顿后的旧持锁者无法被存储层拒绝;etcd 的 revision 天然提供这一性质。这也是很多团队"直接用数据库乐观锁/唯一索引防重"的原因——多数业务其实不需要分布式锁,需要的是幂等。Redis 锁的正确姿势清单:唯一随机值 + SET key value NX PX ttl 一条原子命令;解锁用 Lua 校验 value;TTL 设为临界区安全上界并加看门狗续期(Redisson 的做法);持锁操作必须幂等,把锁当"尽力而为的优化"而非正确性来源。
场景结论:Redis 锁的边界 = 效率型互斥。正确性场景(资金、库存、强一致 leader)换 etcd/ZooKeeper,或下沉为数据库约束。
限流:Redis+Lua vs nginx limit_req vs Envoy RLS vs 云网关
| 维度 | Redis + Lua | nginx limit_req | Envoy RLS(global) | 云 API 网关 |
|---|---|---|---|---|
| 状态分布 | 跨实例共享(天然分布式) | 单机内存,不共享 | 集中式 gRPC 服务(后端多为 Redis) | 平台内置 |
| 算法 | 固定/滑动窗口、令牌桶、漏桶全可写 | 漏桶(毫秒精度 + burst) | 本地令牌桶 + 全局配额 | 通常令牌桶/配额 |
| 失败模式 | Redis 挂了要决定 fail-open/closed | 无外部依赖 | 多一跳 gRPC;RLS 后端同为 Redis,同样面临 fail-open/closed 决策 | 平台托管 |
要点:Redis 官方教程的结论是五种主流限流算法——固定窗口计数器、滑动窗口日志、滑动窗口计数器、令牌桶、漏桶——都可以用 Lua 原子实现,综合评估后推荐滑动窗口计数器作为精度/复杂度/内存的最佳平衡,需容忍突发时用令牌桶(Redis 限流教程)。五种算法与纵轴第二阶段 Brandur 归纳的"固定窗口/漏桶/GCRA"三类口径可以对上:教程五类中未单列 GCRA——按 Brandur 的口径,GCRA 可视为漏桶的高效变体(无 drip、每键只存一个时间戳)。nginx 的 limit_req 依赖节点内共享内存,状态不跨机器——多台 nginx 各限各的,集群总配额失控,它适合做边缘第一道闸而非精确分布式配额(nginx 限流博客)。Envoy 的定位是分层:本地限流吸收突发 + 全局限流调用 gRPC 服务,而后者(envoyproxy/ratelimit)的典型后端正是 Redis——反直觉但重要:Redis 在限流场景不是被替代者,而是被基础设施封装的存储层。实践共识是分层而非二选一:边缘做粗粒度保护 + 应用层(Redis+Lua)做细粒度按租户配额;Redis 的失败模式必须显式决策——常见取舍是 fail-open 以保可用性,代价是限流短暂失效;此为本文基于实践观察的判断而非统计数据,无论选哪一侧都应写进设计文档(见文末建议 5)。
场景结论:分布式精确限流是 Redis 五大场景里最没有争议的主场——竞品要么不做分布式(nginx),要么底层仍用 Redis(Envoy RLS)。边界只在"边缘粗保护"与"平台托管配额"两处让位。
发布订阅:Redis Pub/Sub vs Streams vs NATS vs MQTT vs Kafka
| 维度 | Redis Pub/Sub | Redis Streams | NATS Core | NATS JetStream | MQTT | Kafka |
|---|---|---|---|---|---|---|
| 投递语义 | at-most-once(官方原话) | at-most/least-once | at-most-once | at-least-once | QoS 0/1/2 每消息可选 | at-least-once |
| 离线订阅者 | 消息直接丢失 | 从未消费位置读 | 丢失 | 从流读 | QoS≥1 可补发 | 从 offset 读 |
| 持久化 | 无 | 有(XTRIM 控长度) | 无 | 有 | broker 依赖 | 有 |
| 集群广播成本 | 全集群 bus 广播(7.0 起有 Sharded Pub/Sub 收敛) | 按 key 分片 | 极轻 | 轻 | 轻 | 重 |
要点:Redis 官方文档是边界最清晰的一手来源——"Redis' Pub/Sub exhibits *at-most-once* message delivery semantics … the message is forever lost. If your application requires stronger delivery guarantees, you may want to learn about Redis Streams."(Redis Pub/Sub 官方文档)。Sharded Pub/Sub(7.0+,见纵轴)把消息限制在 key 所属 shard 内广播,是集群模式下的官方扩展路径。与 NATS 的正面对比:Core NATS 与 Redis Pub/Sub 几乎同构,NATS 优势在 subject 拓扑表达力与单二进制运维;需要持久时 NATS 升级 JetStream,对应 Redis 升级 Streams——两个生态是"同一条产品阶梯"(NATS vs Kafka)。MQTT 的不可替代区在 IoT:QoS 每消息可选、为弱网/低功耗设备设计、retained message 与遗嘱消息。Kafka 不是 pub/sub 而是 log:无消费者在线就堆积而非丢弃,适合"事件即资产"的场景。Redis Pub/Sub 在聊天 presence、行情 tick、配置失效广播这类"丢一条不心疼"的流量上仍是延迟与成本最优解。
场景结论:Redis Pub/Sub 的边界 = at-most-once。判断句式只有一句:这条消息丢了你心疼吗?心疼 → Streams/NATS JetStream/MQTT QoS1;不心疼 → Redis Pub/Sub/NATS Core。
成本与运维:一台 Redis 顶五个组件的算账
- 组件收敛收益:五件事共用一个 Redis,省掉四套中间件的部署、监控、备份、升级与团队心智。这是 Redis 在中小系统里"默认在场"的根本原因——竞品要打破的是这个总拥有成本,而不是单项性能。
- 容量账的反面:内存是 Redis 的计费单位。大消息队列堆积、长保留事件流放进 Redis,等于用内存价格买磁盘价值——这是它与 Kafka(磁盘价格买吞吐)在经济模型上的本质差异。
- 故障面账:一个 Redis 同时承担五个职责时,它挂掉 = 缓存击穿 + 任务队列停摆 + 限流失效 + 推送中断四重事故叠加。成熟实践是按"丢了心疼程度"拆实例:缓存与 Pub/Sub 共用可接受,队列与锁必须独立实例——注意"锁的独立实例"只针对留在 Redis 的效率型锁;正确性场景根本不应存在"Redis 锁实例",应整体出走 etcd/ZooKeeper/数据库约束(见锁的场景结论)。
- 托管服务形态:Upstash 把计费单位从"实例小时"改为"请求数",显著降低低流量场景用 Redis 做限流/队列的门槛——反过来巩固了 Redis 模式在 Serverless 时代的默认地位。
决策速查表
| 场景 | 用 Redis 的信号 | 越界信号 | 越界后换谁 |
|---|---|---|---|
| 缓存 | 结构化 value、原子计数、击穿保护 | 纯字符串超高读;每请求必打缓存;超大单机吞吐 | Memcached / Caffeine / Dragonfly |
| 队列 | 同栈异步任务、中等吞吐、允许秒级堆积 | 要 ACK/回溯/中等保留(先升这一档);长保留回放/严格有序/大消息/绝对不丢(才出走) | 先 Redis Streams;仍不满足 → Kafka、RabbitMQ、NATS JetStream |
| 锁 | 防重复执行等效率型互斥 | 失锁即资损/数据损坏、需要 fencing | etcd、ZooKeeper、数据库唯一约束/乐观锁 |
| 限流 | 分布式按用户/租户精确配额 | 仅边缘粗保护;平台级托管配额 | nginx limit_req、Envoy RLS、云网关 |
| 发布订阅 | 实时推送、丢一条无妨、在线即用 | 消息不许丢、离线要补发、每消息 QoS | NATS JetStream、MQTT、Kafka、Redis Streams |
各结论适用的最低版本(速查)
正文各条结论的版本前提散落各处,这里按结论汇总——手上是 6.x/7.x 的读者按行自查:
| 结论 / 特性 | 最低版本 | 对应正文 |
|---|---|---|
| Lua 脚本(原子限流/原子解锁的前提) | 2.6.0(2012-10) | 纵轴第二阶段 |
SET NX PX 单命令加锁 | 2.6.12(2013-03) | 纵轴第二阶段、锁的场景结论 |
| keyspace notifications(缓存失效监听) | 2.8(2013) | 纵轴第二阶段 |
| LFU 淘汰策略 | 4.0(2017) | 纵轴第三阶段 |
| Streams——队列三档阶梯的第二档(消费组/XACK/回溯) | 5.0(2018-10) | 纵轴第三阶段、队列场景 |
| Cluster(pub/sub 全集群广播问题仅在此形态出现) | 3.0(2015-04) | 纵轴第二阶段 |
| client-side caching / RESP3 / 多线程 I/O | 6.0(2020-04) | 纵轴第四阶段 |
| Functions / Sharded Pub/Sub(大集群 pub/sub 收敛) | 7.0(2022-04) | 纵轴第四阶段 |
| 向量/检索并入核心(AGPLv3 回归) | Redis 8.0(2025-05) | 纵轴第五阶段 |
| hash 字段过期、原子 slot 迁移 | Valkey 9.0(2025-10) | 纵轴第五阶段 |
两个注脚:原生 GCRA 限流命令在任何已发布版本中都不存在(截至撰写时仍是提案 issue #17274);Redis 与 Valkey 各自新增的特性互不回补,上表最后两行不可互换。
交叉洞察:两轴合在一起说明了什么
把纵轴(十七年边界如何形成)与横轴(2026 年竞品如何分布)叠在一起,会看到四个单轴看不到的东西。
洞察一:竞品地图是历史争议的沉积层
横轴上每一个"越界后换谁"的格子,都能在纵轴上找到触发它的事件:Kleppmann 2016 年对 fencing token 的批评,沉积为今天 etcd/ZooKeeper 在"锁"格子的稳固生态位;Disque 自曝与 list 队列的丢任务事故,沉积为 Kafka/RabbitMQ 在"队列"格子的分工;pub/sub 的 fire-and-forget 语义,沉积为 NATS JetStream 与 MQTT 的"至少一次"阶梯。换句话说,2026 年的竞品地图不是市场自由竞争的快照,而是 Redis 十七年边界争议的地质剖面——每个专业工具的存在理由,都对应着 Redis 曾被证明"做不到"的一件事。反过来说,读横轴对比表时若不知道纵轴上的事故与争论,就无法理解"越界信号"那一列为什么这么写。
洞察二:边界由一个变量统一决定——丢失/双主的代价 vs 延迟收益
纵轴的每次关键争论(Redlock 之争、Disque 自省、Streams 诞生)表面上分属不同场景,实际上都在测量同一个变量:当系统出错时,你损失的是"多干一遍活"还是"数据本身"? 横轴五个场景的对比也收敛到同一判断式。两条轴在 2026 年汇合成一句可操作的口诀:先问数据的丢失/双主代价,再问延迟收益;代价高就出 Redis,代价低就留在 Redis。 缓存丢了可以回源(代价低→主场),限流失效可以 fail-open(代价低→主场),作业队列丢了重跑(代价低→甜点区),锁的双主意味着资金错账(代价高→出走),事件流丢失意味着审计断裂(代价高→出走)。五种用法的"边界硬度"差异,本质上是"出错代价"的连续谱,而非五种独立的经验规则。
洞察三:官方修补过的地方边界软,没修补的地方边界硬
把两轴按"官方是否出手"重新排列,会发现一个清晰的分层:凡是官方用新原语修补过的场景,边界内侧都留有升级路径,用户可以不离开 Redis;凡是官方从未修补的场景,边界是硬的,必须出走。 缓存(LRU→LFU→client-side caching)与队列(list→Streams 消费组)是纯官方收编;限流则是官方与社区共同推进——官方 Lua 教程收编了原子化算法模式,社区模块 redis-cell(Brandur 个人编写,见纵轴第三阶段)把 GCRA 工程化,原生 GCRA 命令仍是提案(issue #17274,未发布)——三者共同点在于修补都落在 Redis 边界内侧,所以"越界"更多是规模问题而非正确性问题;而锁的正确性缺口十七年从未被官方修补——Redlock 至今拿不出 fencing token,官方文档只是把 Kleppmann 的警告原样收录;pub/sub 的 at-most-once 也从未改变,官方文档直接承认"the message is forever lost",Sharded Pub/Sub 修的只是广播风暴这个性能问题,不是投递语义。对用户的含义:遇到"该不该用 Redis 做锁/pub/sub"的疑问,不要期待下一个版本会帮你解决——这两个缺口是架构级的,等不来补丁。
洞察四:"Redis 模式"与"Redis 产品"已分离:学模式,不绑定产品
纵轴的许可证地震(2018 Commons Clause → 2019 RSALv2 → 2024 RSALv2/SSPLv1 → 2025 AGPLv3 回归)与横轴的阵营分布(Valkey 被 AWS/Google/Oracle 接纳、KeyDB 停更、Garnet 无模块生态)合在一起指向同一个事实:五种用法构成的模式知识是可迁移资产,而具体产品是可替换的选型项。 五种用法涉及的核心命令与 RESP 协议在 Redis 与 Valkey 两边保持兼容,各自新增的特性互不回补(Redis 8 的向量/检索与 Valkey 9.0 的 hash 字段过期、原子 slot 迁移分属两条线,见纵轴第五节);RESP 协议的稳定使 Valkey/Dragonfly/Garnet 全部直接继承 Redis 的客户端生态——你按本文边界学到的"什么时候用 Streams、什么时候出走到 Kafka",在分叉之后依然成立;变化只发生在"谁替你运维、按什么计费、许可是否安心"这一层。因此团队技能投资的正确姿势是:把本文的边界判断写成团队的选型文档(与具体产品解耦),把"Redis vs Valkey"留给采购与法务去决策。
对用户的判断与建议
综合两轴,给出一份可执行的决策流程:
- 默认姿势:中小系统直接用一个 Redis 承担缓存 + 限流 + 作业队列(BullMQ/Sidekiq)+ pub/sub,享受组件收敛收益——这是十七年历史反复验证的默认架构。
- 立刻检查三处硬边界:(a) 有没有用
SETNX+EXPIRE两步加锁或拿 Redlock 守资金/库存——有则换数据库唯一约束或 etcd;(b) 有没有用 List 队列跑"不能丢"的任务——有则升级 Streams 或 MQ;(c) 有没有拿 pub/sub 传"不许丢"的消息——有则换 Streams/JetStream。 - 规模触发出走,而非功能触发:队列要天级保留/回放/日亿级消息才上 Kafka;缓存纯字符串超高读才换 Memcached;这些出走决定由横轴的量化信号触发,不由"听起来更专业"触发。
- 按"心疼程度"拆实例:一个 Redis 挂掉不应同时引发缓存击穿、队列停摆、限流失效、推送中断——队列与锁必须独立实例,缓存与 pub/sub 可以共用("锁的独立实例"仅指效率型锁;正确性场景直接执行建议 2(a) 的 etcd/ZooKeeper/数据库约束,根本不建"Redis 锁实例")。
- 限流显式决策 fail-open/closed:Redis 不可用时放行还是拒绝,必须写进设计文档,而不是等故障现场临时决定。
- 压测验证厂商宣称:Dragonfly 的 25 倍 QPS 是厂商口径,目前可查的独立基准(横轴所引 Reddit 社区基准帖)只复现出 1.2~3 倍量级,且样本有限——任何"换掉 Redis"的性能决定,用你自己的负载实测后再做。
参考来源
发展史(纵轴)
- Wikipedia: Redis(版本史、许可证史、antirez 任职与离任时间线)
- antirez 旧博客: Redis as an LRU cache
- Instagram Engineering: Storing hundreds of millions of simple key-value pairs in Redis
- Ruby Toolbox: resque
- Linux.com: Redis 2.6.0 released with Lua scripting support、antirez.com/news/52
- redis.io: 分布式锁页面、antirez.com/news/101: Is Redlock safe?(本会话直接抓取核实)
- antirez.com/news/88: Adventures in message queues(Disque)
- brandur.org/rate-limiting(本会话直接抓取核实)、brandur/redis-cell
- Martin Kleppmann: How to do distributed locking
- redis.io/blog/redis-5-0-is-here、Devopedia: Redis Streams
- redis.io/blog/redis-6-0-ga、antirez.com/news/130: Client side caching in Redis 6
- antirez.com/news/133: The end of the Redis adventure、The Register 报道
- redis.io/blog/welcome-back-to-redis-antirez、antirez.com/news/151: Redis is open source again、redis.io/blog/agplv3
- redis.io/blog/redis-82-ga、redis.io/blog/redis-8-4-open-source-ga
- Phoronix: Linux Foundation Valkey、Linux Foundation: A Year of Valkey
横向对比(横轴)
- redis.io/blog/redis-8-ga、Redis 8.0 发布说明
- redis.io: Pub/Sub 官方文档(at-most-once 原文、Sharded Pub/Sub——横轴章节直接读取全文)、Keyspace notifications
- Redis Streams 官方教程
- Redis 限流官方教程
- Linux Foundation: Valkey 9.0 新闻稿、valkey.io、valkey-io/valkey
- AWS: Redis vs Memcached、ScaleGrid 2025 对比
- AWS: Kafka vs RabbitMQ、bullmq.io、Judoscale: Node.js 作业队列选型
- Medium: RabbitMQ / Kafka / Redis 对比
- antirez 回应(HN #11022176)、实现分布式锁的正确姿势(fencing token 综述)
- nginx 限流官方博客、limit_req 模块文档
- Envoy 全局限流架构文档、envoyproxy/ratelimit
- Dragonfly 官网(25x/内存/成本宣称)、Reddit 社区基准:Redis vs Valkey vs DragonflyDB vs KeyDB
- arXiv 2510.19805《Next Generation Cloud-native In-Memory Stores》(KeyDB 停更、Garnet/Valkey 评估)
- NATS vs Kafka 对比、Reddit: Kafka/Rabbit/NATS 讨论
- Redisson: Kafka vs Redis 可靠 Pub/Sub
- Hacker News: Redis 4.0 讨论、antirez.com/news/81: Thanks Pivotal, Hello Redis Labs