db-transaction
No related notes
Outlinks (0)
No outlinks found
Backlinks (0)
No backlinks found
1 · db-transaction#
事务是在并发环境下,为一组读/写操作划出一个边界,保证这组操作要么整体生效、要么整体撤销,并且在执行期间按某种隔离规则看到一个自洽的数据视图。
S 应用里的一个业务动作,通常不是一条 SQL,而是多条读写语句组成的一次状态变更;同时系统又处在高并发环境里,很多事务会同时碰到同一批数据。
C 如果没有事务,这些步骤可能只做了一半就失败,也可能在并发下互相看见未提交数据、重复读取到不同结果,甚至把彼此的更新覆盖掉。
Q 所以事务到底在保护什么?ACID、隔离级别、一致性读、MVCC、锁分别解决的是哪一层问题?在真实 SQL 里看到的阻塞、死锁、幻读、长事务拖垮系统,和这些概念之间到底怎么连起来?
A 事务本质上是在给“多步状态变更”划定一个正确性边界:要么整体生效,要么整体撤销;并发时则通过版本、可见性规则、锁或冲突检测,让每个事务看到一个被约束的数据库世界。
不同数据库虽然都叫“事务”,但底层实现并不一样:Postgres 倾向用多版本行和冲突检测,MySQL / InnoDB 倾向用 undo log、read view 和锁。 后面按“先问题,再保证,再实现,最后回到 SQL 现象”的顺序整理。
- 事务到底在保护什么
不是
begin / commit语法 而是多步状态变更的正确性边界 - ACID 用人话重讲 A:失败不能留半成品 C:结果不能破坏约束和业务不变量 I:并发时每个事务都像活在一个受控视图里 D:提交后的结果,掉电恢复后也不能丢
- 一致性读到底想解决什么 为什么事务不能直接看“当前物理值” 什么叫一个事务自己的数据快照 为什么这件事是并发控制的基础
- 数据库怎么保存“旧世界”
Postgres:多版本行(
xmin / xmax) MySQL / InnoDB:trx_id、roll_pointer、undo 版本链 read view / 可见性判断怎么决定你能看到哪个版本 - 隔离级别到底在放开什么限制 Read Uncommitted Read Committed Repeatable Read Serializable
- 并发异常到底是什么 脏读 不可重复读 幻读 丢失更新 各自对应被哪个隔离级别放行或禁止
- RC 和 RR 到底差在哪 read view 什么时候生成 为什么 RC 每次读可能变 为什么 RR 的一致性读更稳定
- 一致性读 vs 当前读
普通
SELECTSELECT ... FOR UPDATEUPDATE / DELETE为什么“读历史版本”和“锁当前记录”是两套机制 - 为什么有 MVCC 还要锁 MVCC 解决的是“怎么看” 锁解决的是“现在这条边界谁能改”
- 行锁、间隙锁、next-key lock 锁的不是抽象“行” 而是索引记录和索引区间 为什么这和幻读、范围更新直接相关
- 并发写冲突怎么收场 MySQL / InnoDB:锁、等待、死锁检测 Postgres:Serializable Snapshot Isolation / 冲突后回滚重试 为什么应用层要准备 retry
- 死锁为什么发生 循环等待 锁顺序不一致 范围锁带来的复杂性
- 长事务为什么危险 undo 清理拖住 版本链变长 锁持有时间长 系统吞吐下降
- 事务提交到底发生什么 undo / redo 各自负责什么 WAL / crash recovery 在保护什么 “提交成功”在存储层意味着什么
- 把事务原理映射回 SQL 现象 为什么会阻塞 为什么会死锁 为什么 RR 下范围更新容易卡 为什么长事务会拖垮系统
1.1 · 正文骨架
- 事务保护的不是某一条 SQL,而是一次业务动作的正确性边界
- 事务主要在兜底两类风险:失败和并发
- 并发的核心难点,是每个事务执行时到底看见哪个数据库世界
- 一致性读背后真正需要的是版本;所谓旧世界,本质上就是旧版本
- 隔离级别本质上是在定义你愿意接受多少并发扰动
- 在 InnoDB 里,RC 和 RR 的关键差异,可以落到 read view 的生成时机
- MVCC 解决的是读一致性;真正的读写冲突和写写冲突,还需要锁来处理
- 锁住的不是抽象的行,而是索引记录和索引区间
- 死锁和长事务,是事务机制为正确性付出的系统代价
- redo、undo 和 crash recovery,负责把提交结果真正落稳