第4章 原子性与消息传递


文档摘要

第4章 原子性与消息传递 章节摘要:本章跟着一个抢购脚本走。多个客户端并发修改同一批键,谁保证中间状态不被别人看见?答案依次是事务队列、Lua 脚本、订阅通知——三种不同粒度的原子与解耦方案,各自对应一类并发问题。 一条主线 秒杀场景:库存 100 件,五千个请求同一毫秒到达。判断库存、扣减、记录买家三步必须整体执行,否则超卖。本章顺着"怎么把多步焊成一步"展开:先看 MULTI 事务能焊到什么程度(它不回滚,能干什么不能干什么要说清),再看 Lua 脚本这个终极方案(判断逻辑进脚本,条件原子化),最后跳出抢购看发布订阅——问题从"互斥"变成"通知"。 沿途站点 4.1 事务:命令入队、EXEC 一次执行,队列机制与 WATCH 乐观锁。 4.

第4章 原子性与消息传递

章节摘要:本章跟着一个抢购脚本走。多个客户端并发修改同一批键,谁保证中间状态不被别人看见?答案依次是事务队列、Lua 脚本、订阅通知——三种不同粒度的原子与解耦方案,各自对应一类并发问题。

一条主线

秒杀场景:库存 100 件,五千个请求同一毫秒到达。判断库存、扣减、记录买家三步必须整体执行,否则超卖。本章顺着"怎么把多步焊成一步"展开:先看 MULTI 事务能焊到什么程度(它不回滚,能干什么不能干什么要说清),再看 Lua 脚本这个终极方案(判断逻辑进脚本,条件原子化),最后跳出抢购看发布订阅——问题从"互斥"变成"通知"。

沿途站点

  • 4.1 事务:命令入队、EXEC 一次执行,队列机制与 WATCH 乐观锁。
  • 4.2 Lua 脚本:脚本即一条命令,条件判断与写入在同一原子单元里完成。
  • 4.3 发布订阅:一张反向的订阅链表,消息即时推送给所有频道挂载者。

三者的能力边界:

拐点与结论

两个转折点。第一个在 4.1:Redis 事务没有回滚,执行期出错前面的命令照样生效——这与关系型数据库的直觉相反,理解它才能用对 WATCH。第二个在 4.2:Lua 不是"高级事务",而是"把业务逻辑搬进服务端执行",脚本慢就是全局慢,原子性的代价是共享了那唯一的线程。

本章知识点清单

  • 说清 MULTI 到 EXEC 之间服务端发生了什么,QUEUED 的含义与隔离性的真正来源
  • 用"入队错误拒绝整个事务、执行错误只废一条"这两条规则预判事务行为
  • 写出 WATCH 加重试的乐观锁循环,并指出它在高竞争下失效的原因
  • 解释"脚本即命令":整段 Lua 在主线程原子执行,判断与写入之间无窗口
  • 列出 Lua 脚本的三条纪律:短、不写随机后写键、缓存用 EVALSHA
  • 区分发布订阅与 Stream 的可靠性边界,说出"消息丢了要不要紧"这句判据

三种工具对应的故障现场,先在脑子里挂上号:

现象 根因层 对的工具
两步写被并发插队,出现脏数据 原子性缺口 MULTI/EXEC 或 Lua
先读后写,判断依据已过期 读写到写之间的窗口 Lua(判断入脚本)
抢锁解锁,删了别人的锁 释放条件不原子 Lua 对比再删
一份数据要通知多个在线方 通知而非任务 发布订阅
任务不能丢、可重试、多下游独立消费 可靠投递 Stream

这张表也是排错时的分诊台:同一个"数据不对了"工单,按表现定位到行,工具与章节就都出来了。

学本章时有个容易走偏的倾向要先拦住:不是所有并发问题都要上重型工具。多数"写写冲突"其实靠好的键设计就能消掉——把共享的一个计数键拆成按请求方隔离的多个键,冲突自然消失;真正需要原子语义的是"多键必须保持一致"或"读后写不可分"的场景。先问能不能改设计,再问用哪把锁,这个顺序能省掉一半的复杂度。

本章的动手主线是同一个秒杀场景的三轮改造:第一轮用 pipeline 裸写(制造超卖)、第二轮上 WATCH 事务(压住低并发、重试风暴现形)、第三轮进 Lua(彻底焊死)。三轮跑下来,三种工具的能力边界不是背下来的,是被数据打出来的。

读完你应该

  1. 说清 MULTI/EXEC 的入队机制与"不回滚"设计的原因
  2. 用 WATCH 实现乐观锁并解释重试逻辑
  3. 写出判断加扣减的秒杀 Lua 脚本并评估其风险
  4. 区分发布订阅与 Stream 的可靠性边界
  5. 面对并发需求能按上面决策图选对工具

下一章的接力

原子性管住了"写与写"的竞争,但内存不是无限的。第 5 章讲键的死亡:过期怎么删、内存满了淘汰谁——那是另一套精巧的结构与算法。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U