知识卡片
快照隔离靠MVCC事务ID可见性规则实现读不阻塞写
内容
[[读已提交隔离级别防脏读脏写的两种实现方式]]无法防止”读取偏差”:同一个事务内先后 两次读取,可能因为中间有别的事务提交了改动而看到不一致的状态(比如Alice看自己两个 账户,正好撞上一笔跨账户转账进行到一半,感觉”钱凭空消失了100美元”)。这种暂时的 不一致对交互式用户或许无伤大雅,但对备份、分析查询、完整性检查这类需要长时间扫描 大量数据的操作是致命的——如果扫描过程中数据一直在变,结果可能毫无意义。快照隔离 解决这个问题的思路是:让每个事务从数据库的一个一致快照读取,即事务开始那一刻已经 提交的所有数据,之后哪怕这些数据被别的事务改了,本事务全程只看这份快照。实现上, 快照隔离沿用了读已提交”记住旧值和新值”的思路并加以推广——多版本并发控制(MVCC): 数据库给每个事务分配一个永远递增的事务ID,每行数据带有created_by(创建该行的事务 ID)和deleted_by(删除该行的事务ID,初始为空)两个字段,UPDATE在内部被翻译成 DELETE+INSERT。可见性判定规则:一个对象对某个读事务可见,当且仅当创建它的事务在 读事务开始时已经提交,且它没被标记删除,或者标记删除它的事务在读事务开始时还没 提交。这套机制的关键性能特性是”读不阻塞写,写不阻塞读”:写入照常用锁防脏写,但 读取完全不需要锁,只需按可见性规则挑出正确版本,因此快照隔离下可以在正常处理写入 的同时高效执行长时间的一致性查询,两者之间没有任何锁争用。
结构图:
flowchart TD
A[每个事务分配永远递增的事务ID] --> B[每行带created_by/deleted_by字段]
B --> C[UPDATE内部翻译为DELETE+INSERT]
D[读事务判断对象可见性] --> D1[创建该对象的事务在读事务开始前已提交]
D --> D2[未被标记删除, 或删除事务读事务开始时未提交]
D1 --> E[对象可见: 构成读事务的一致快照]
D2 --> E
F[写入照常用锁防脏写] -.读取无需任何锁, 只按可见性规则取版本.-> G[读不阻塞写, 写不阻塞读]
参考来源
- 位置:《数据密集型应用系统设计》第七章《事务》"快照隔离和可重复读""实现快照隔离"
"观察一致性快照的可见性规则"(源文件:_epub-src/ch7_split_002.html)
- 结论依据:原文详述MVCC用事务ID标记行的created_by/deleted_by,UPDATE翻译为
DELETE+INSERT,给出对象可见性的两条判定规则,并强调快照隔离的关键性能特性是
"读不阻塞写,写不阻塞读",直接支撑本卡片的结构梳理。
- 原始内容:当一个事务开始时,它被赋予一个唯一的,永远增长的事务ID……表中的
每一行都有一个created_by字段……每行都有一个deleted_by字段……如果以下两个条件
都成立,则可见一个对象:读事务开始时,创建该对象的事务已经提交……从性能的
角度来看,快照隔离的一个关键原则是:读不阻塞写,写不阻塞读。