知识卡片
读已提交隔离级别防脏读脏写的两种实现方式
内容
最基本的事务隔离级别”读已提交”提供两个保证:没有脏读(读不到其他事务尚未提交的 数据)和没有脏写(不会覆盖其他事务尚未提交的数据)。防脏写通常用行锁实现:事务 要改一个对象前必须先拿到该对象的锁,一直持有到提交或中止,另一个事务想写同一对象 就必须排队等待——这是读已提交(或更强隔离级别)数据库都会自动做的。防脏读理论上 可以用同一把锁:想读的事务也去获取锁、读完立刻释放,这样读的时候对象肯定不处于 “脏”的中间状态。但这个方案在实践中效果很差:一个耗时长的写事务会拖住所有只读事务 的锁等待,严重损害只读查询的响应时间,且这种迟缓可能通过连锁效应波及应用其他 部分。因此绝大多数数据库改用另一种更实用的方式:对每个被写入的对象,数据库同时 记住”旧的已提交值”和”当前持有写锁的事务正在设置的新值”,事务进行期间,其他读取 这个对象的事务一律拿到旧值,只有当新值真正提交之后,后续读取才切换到新值——这样 读操作完全不需要等锁,只是被引导去读一份”暂存的旧快照”。这个”给写入对象保留一份 旧值供并发读取”的思路,正是后文快照隔离/MVCC机制的雏形,读已提交可以理解成 “只在单条读取的粒度上做类似的事”,而快照隔离是把这个思路扩展到整个事务的生命周期。
参考来源
- 位置:《数据密集型应用系统设计》第七章《事务》"实现读已提交"(源文件:
_epub-src/ch7_split_002.html)
- 结论依据:原文说明读已提交通常用行锁防止脏写,防脏读若用读锁会因长写事务拖慢
只读事务响应时间而效果不好,因此大多数数据库改为记住对象的旧已提交值和新值、
让并发读取拿旧值直到新值提交,直接支撑本卡片结论。
- 原始内容:最常见的情况是,数据库通过使用行锁来防止脏写……要求读锁的办法在
实践中效果并不好……大多数数据库使用图7-4的方式防止脏读:对于写入的每个对象,
数据库都会记住旧的已提交值,和由当前持有写入锁的事务设置的新值……只有当新值
提交后,事务才会切换到读取新值。