第3章 事务与并发控制

事务与并发控制是数据库内核面试的核心,约占大厂面试题的 35%。本章从 ACID 实现机制、MVCC 原理、死锁处理三个高频考点切入,配合真实项目案例,帮你构建从理论到工程的回答体系。

本章重点:① 原子性/持久性的日志实现差异;② MVCC 版本链与 read view 时机; ③ wait-for graph 检测与牺牲者选择。
ACID 实现机制 原子性 undo log 持久性 redo log + WAL 一致性 约束 + 日志 隔离性 锁 + MVCC ▸ 面试回答时先总述 ACID,再逐一展开实现细节

📌 面试问题 1 — 事务 ACID 特性与 InnoDB 实现

🎯 面试官考察点
  • 是否理解 ACID 四个特性的语义差异
  • 是否掌握 undo log / redo log / 锁 / MVCC 等底层机制
  • 能否将理论与工程组件对应起来
📐 回答框架
  1. 一句话总述 ACID 是事务的四大保证
  2. 原子性 → undo log(回滚时撤销操作)
  3. 一致性 → 约束检查 + 日志 + 并发控制共同保证
  4. 隔离性 → 锁 + MVCC 提供四种隔离级别
  5. 持久性 → redo log + WAL 策略确保提交不丢失
💡 示例回答(85字)

ACID 是事务四大特性。原子性通过 undo log 实现,回滚时撤销已执行操作;一致性由约束、日志和并发控制共同保证;隔离性通过锁和 MVCC 实现;持久性依靠 redo log 和 WAL 策略,确保提交后数据不丢失。

🔍 可能追问
  • undo log 和 redo log 在崩溃恢复时的协作流程是怎样的?
  • 双写缓冲(doublewrite buffer)如何保证持久性?
⚠️ 避坑提醒

不要只背诵 ACID 定义而忽略实现机制;特别注意区分 undo log 负责原子性、redo log 负责持久性,不可混淆;提到隔离性时必须关联锁与 MVCC 两种手段。

📌 面试问题 2 — MVCC 实现原理与隔离级别差异

🎯 面试官考察点
  • 对 MVCC 底层组件(事务 ID、版本链、read view)的理解
  • RC 与 RR 级别下 read view 创建时机的差异
  • MVCC 如何解决读写冲突而不阻塞
📐 回答框架
  1. MVCC 核心目标:读写不互斥
  2. 三个组件:事务 ID、undo log 版本链、read view
  3. 每行记录隐藏字段 db_trx_id / db_roll_ptr 构成版本链
  4. read view 创建时机:RC 每次查询创建,RR 首次查询创建
  5. 可见性判断:比较事务 ID 与 read view 中的活跃事务列表
💡 示例回答(88字)

MVCC 通过维护数据行多版本实现读写不互斥。每行隐藏事务 ID 和回滚指针构成版本链。read view 是可见性快照,RC 每次查询创建新 read view,可读到已提交更新;RR 仅首次查询创建,实现可重复读。

🔍 可能追问
  • MVCC 在 RR 级别下如何避免幻读?(gap lock 配合)
  • 长事务对 MVCC 版本链有什么负面影响?
⚠️ 避坑提醒

不要只说概念不提组件;必须清晰区分 RC 和 RR 的 read view 创建时机;MVCC 不能单独解决幻读,RR 级别需结合 gap lock;避免将 MVCC 与锁机制完全割裂。

MVCC 原理 版本链结构 read view RC vs RR 可见性判断 ← 面试追问方向:从原理 → 结构 → 时机 → 差异对比

📌 面试问题 3 — 死锁检测算法与处理策略

🎯 面试官考察点
  • 是否理解死锁产生的四个必要条件
  • 是否掌握 wait-for graph 等检测算法
  • 工程上如何选择牺牲者并优化
📐 回答框架
  1. 死锁四个必要条件(互斥/持有等待/不剥夺/循环等待)
  2. 检测方法:超时检测 vs wait-for graph(有向图检测环)
  3. 处理策略:选择回滚代价最小的事务作为牺牲者
  4. 工程优化:规范化加锁顺序、锁超时、死锁检测阈值
💡 示例回答(82字)

死锁检测常用 wait-for graph 算法,检测有向环即死锁。选择回滚代价最小的事务作为牺牲者释放锁。工程上通过规范化加锁顺序、设置锁超时、死锁检测阈值等优化降低死锁发生概率。

🔍 可能追问
  • 如何量化"回滚代价最小"?(事务已执行时间/修改行数/日志量)
  • 分布式事务中的死锁检测与单机有何不同?
⚠️ 避坑提醒

不要只讲四个理论条件而忽略检测算法;wait-for graph 要说明"节点=事务、边=等待关系、环=死锁";提到处理策略时必须包含"选择牺牲者"的考量维度。

🏗 项目案例:高并发交易系统的 MVCC 并发控制优化

业务背景

金融核心交易系统,日均处理千万级交易,高峰期存在严重读写冲突导致吞吐量急剧下降。

规模指标

TPS 50,000+,数据量 10 TB+,日均交易量 2,000 万笔,峰值 QPS 12 万。

技术栈

MySQL 8.0 InnoDB、RocksDB、分布式事务中间件(DTX)、Prometheus + Grafana 监控。

核心难点
  1. 热点账户并发更新导致锁竞争激烈,TPS 跌至 8,000
  2. 长事务导致 MVCC 版本链膨胀,undo 表空间暴涨
  3. 读写冲突引发大量事务回滚,系统抖动频繁
面试高频追问
  • 热点行拆分后如何保证全局一致性?(分布式事务/异步对账)
  • MVCC 版本链膨胀如何监控和治理?(undo 表空间阈值 + 长事务 kill)
  • 降级 RC 隔离级别后如何弥补业务语义?(业务幂等 + 乐观锁重试)
可迁移回答话术(95字)

我们通过分析慢查询和锁等待日志,定位热点账户为瓶颈。采用三方面优化:热点行拆分子账户分散锁竞争;监控长事务并设超时阈值防版本链膨胀;业务允许场景降级 RC 隔离级别减少 gap lock。最终 TPS 从 1.2 万提升至 5 万+。

📊 核心知识对照表:事务隔离级别与实现机制
隔离级别 脏读 不可重复读 幻读 内核实现机制
READ UNCOMMITTED 可能 可能 可能 不加锁,直接读取最新版本
READ COMMITTED 避免 可能 可能 MVCC + 行锁,每次查询创建 read view
REPEATABLE READ 避免 避免 可能(InnoDB gap lock 可避免) MVCC + gap lock / next-key lock
SERIALIZABLE 避免 避免 ← 上一章 📖 返回目录 下一章 →