Stay Hungry,Stay Foolish!

从 Redo Log 到秒杀:我对数据库并发控制的一次彻底复盘

从 Redo Log 到秒杀:我对数据库并发控制的一次彻底复盘

https://github.com/fanqingsong/seckill

refs:

https://github.com/fanqingsong/seckill-demo

https://github.com/fanqingsong/itstack-demo-design

 

image

 

 

 

image

 

 

太棒了,这个需求来得太及时了 👍
我们把前面的讨论从“聊天”升级为“博客级总结”,既保留深度,又适合对外输出。
下面我给你整理了一份结构清晰、逻辑递进、可直接发表的总结内容,你只需按自己风格润色即可。

从 Redo Log 到秒杀架构:我对数据库并发控制的完整梳理

本文基于一次深度技术讨论整理而成,涵盖了事务、日志、MVCC、锁机制以及高并发场景下的架构设计。希望能帮你把零散的知识点串联成体系。

一、开篇:数据库是如何“记住”一切的?

事务是数据库的灵魂,而 ACID​ 是事务的四大支柱。在 InnoDB 中,这套机制并不是靠单一组件实现的,而是靠一套精密的协作体系。

1. 持久性(Durability):Redo Log 的安全网

为了保证“已提交的事务绝不丢失”,InnoDB 采用了 WAL(Write-Ahead Logging,预写日志)​ 机制。
  • Redo Log​ 记录的是数据页的物理变更(“在某个偏移量写了什么值”)。
  • 事务提交时,必须先将 Redo Log 从内存刷入磁盘(fsync)。
  • 即使数据页还没来得及写入磁盘就发生断电,重启后依然可以通过 Redo Log 重做(Redo)​ 已提交的修改。

2. 原子性(Atomicity):Undo Log 的后悔药

  • Undo Log​ 记录的是数据的旧值。
  • 当事务执行失败或调用 ROLLBACK 时,利用 Undo Log 将数据恢复到修改前的状态。
  • 同时,Undo Log 也是 MVCC(多版本并发控制)​ 的基石。

二、隔离性(Isolation):并发控制的两套武器

隔离性解决的是“多个事务同时执行时,如何互不干扰”。InnoDB 主要依靠 MVCC​ 和 锁​ 两套机制。

1. MVCC:读写互不阻塞的魔法

MVCC 的核心思想是:为数据维护多个历史版本。
  • 隐藏字段:每行记录包含 trx_id(事务ID)和 roll_ptr(指向 Undo Log 的指针)。
  • Read View(快照):事务启动时生成一个“快照”,之后的普通 SELECT 都基于这个快照读取历史版本。
  • 快照读 vs 当前读:
    • 普通 SELECT​ 是快照读,利用 MVCC 无锁读取,性能极高。
    • UPDATE / DELETE / SELECT FOR UPDATE​ 是当前读,必须读取最新版本并加锁,绕过 MVCC。

2. 锁机制:解决写冲突的底线

当 MVCC 无法满足强一致性要求时,就需要锁出场。
  • 共享锁(S 锁 / 读锁):允许多个事务同时读取,但阻止其他事务修改。适用于“我读的时候,别人别改”的场景。
  • 排他锁(X 锁 / 写锁):事务独占资源。UPDATE 和 SELECT FOR UPDATE 加的都是 X 锁,强度最高,会阻塞其他一切的读写尝试。

三、并发控制的哲学:乐观 vs 悲观

理解了底层锁,我们上升到设计思想层面:乐观并发控制与悲观并发控制。
 
维度
悲观并发
乐观并发
核心思想​
假设冲突一定会发生,先上锁,后处理。
假设冲突概率很低,先处理,提交时校验。
实现手段​
依赖数据库的 S/X 锁。
依赖 版本号、CAS​ 或 业务状态机。
冲突处理​
阻塞等待,直到获取锁。
提交失败,应用层重试。
适用场景​
金融核心、库存扣减(强一致)。
配置更新、用户资料修改(高并发)。
关键认知:MVCC 本质上是一种乐观的读策略(无锁读),而数据库的写操作永远是悲观的(必须加锁)。

四、实战升华:秒杀架构中的一致性博弈

理论最终要服务于实战。以“秒杀库存”为例,我们看到了不同技术方案的权衡。

1. 为什么不能直接用数据库锁?

纯悲观锁(FOR UPDATE)在秒杀场景下是灾难:
  • 10 万 QPS 的请求会导致大量线程阻塞,数据库连接池瞬间耗尽。
  • 行锁竞争激烈,RT(响应时间)飙升。

2. 正确的架构:缓存预扣 + 异步落库

成熟的秒杀方案通常采用 乐观思想 + 最终一致性:
  1. Redis 原子扣减:利用 Redis 单线程和 Lua 脚本,在内存中完成库存的极速预占。这是抗并发的关键。
  2. 消息队列(MQ)解耦:将扣减成功的请求放入队列,削峰填谷。
  3. 数据库最终确认:消费者异步读取 MQ,利用数据库的 悲观锁(FOR UPDATE)​ 进行最终的库存校验和扣减,保证数据的绝对准确。

3. 链路中的身份传递

在整个分布式链路中,用户 ID 和订单 ID 必须通过消息体在系统中传递,而不是依赖下游去 Redis 反查。这样才能保证 Redis、MQ 和数据库三者之间的数据一致性。

五、结语

从 Redo/Undo Log 的物理实现,到 MVCC 的版本链,再到 S/X 锁的互斥逻辑,最后落地到秒杀架构的高并发设计。数据库技术就像一个精密的钟表:
  • Redo Log​ 保证了即使摔在地上(宕机),指针也不会乱走;
  • MVCC​ 让读指针和写指针互不干扰;
  • 锁​ 则在必要时强制让指针停下来,等待同步。
理解这些底层原理,不仅能帮我们写出更健壮的代码,更能让我们在面对高并发挑战时,做出最合理的技术选型。

📝 博客写作建议

  1. 配图:建议配上 Redo Log 循环写入图、MVCC 版本链图​ 和 秒杀架构流程图,会非常加分。
  2. 代码块:文中的 SQL 和 Lua 脚本建议用代码块高亮展示。
  3. 标题优化:你可以根据博客平台的调性,把标题改得更吸引人,例如:
    • 《别再死记硬背 ACID 了,聊聊 InnoDB 背后的生存法则》
    • 《从 Redo Log 到秒杀:我对数据库并发控制的一次彻底复盘》
需要我再帮你提炼几个适合发朋友圈或公众号的金句吗?😄

 

posted @ 2026-09-25 23:30  lightsong  阅读(18)  评论(0)    收藏  举报
千山鸟飞绝,万径人踪灭