《DDIA》读书笔记:事务

本文是第七章Transaction部分的读书笔记。

这部分包括的内容为:

  • ACID的含义
  • 读已提交和它存在的问题
  • 快照隔离(可重复读)和它存在的问题
  • 可串行化的实现方法

个人感觉这部分的亮点在于对 lost update 和 Write Skew and Phantoms 的分析.
我的总结就是3种情况: read-modify-write, read-compare-update, read-compare-insert

ACID

Atomic - 没有中间状态,要么成功,要么失败
Consistency - consistency refers to an application-specific notion of the database being in a “good
state". 从应用的角度要求数据库处于正确的状态
Isolation - concurrently executing transactions are isolated from each other.
Durability - once a transaction has committed successfully, any data it has written will not be forgotten

读已提交

  • When reading from the database, you will only see data that has been committed (no dirty reads). 不存在赃读
  • When writing to the database, you will only overwrite data that has been com‐ mitted (no dirty writes). 不存在赃写

问题:事务内两次相同的读操作可能得到不同的结果,即不可重复读

快照隔离(可重复读)

原则是读和写之间不冲突,通常用MVCC实现。存在的问题: lost update, Write Skew and Phantoms

问题1: lost update. 两个线程同时执行read-modify-write的流程,有一个线程的修改会被另一个线程覆盖掉。例如2个线程同时向一个账户转账200元,转账前账户余额为500元,线程都将更新的值设为700,那么账户的最终余额为700元,丢失了200元

解决

  • 使用原子更新操作。UPDATE counters SET value = value + 1 WHERE key = 'foo';
  • read的时候显式的加锁,避免其他线程读
  • 自动检测lost update的发生,如果会发生更新丢失,abort当前transaction。pg的RR有,InnoDB的RR没有
  • compare-and-set. UPDATE wiki_pages SET content = 'new content' WHERE id = 1234 AND content = 'old content';但要求compare的必须是最新的数据,例如RR隔离级别下的MySQL,content='old content'是快照读,是读不到最新数据的

问题2: Write Skew and Phantoms.

发生的情况为read-compare-write,流程如下

  • 1 读数据
  • 2 根据读出的数据判断条件是否符合
  • 3 如果符合就执行写操作
  • 但第3步的写操作会让第2步的条件由符合变为不符合

具体可以分为read-compare-update和read-compare-insert两种情况

  • read-compare-update. 有三名医生A、B、C,要求任意时刻至少有一名医生是未被预约的。A已被预约,然后来了用户1和2,1看到B和C都可以被预约,满足可预约条件,就预约了B,2同样看到B和C都可以被预约,就预约了C,1和2都执行完后,3名医生都被预约了,就违背了最开始的要求。这种情况可以在read时加锁来解决这个问题,SELECT FOR UPDATE
  • read-compare-insert. 为了保证用户名唯一,为用户分配用户名时,先判断用户名是否存在,如果不存在就插入该用户名,结果就是并发执行的情况下,多个记录有相同的用户名。MySQL的Next-key Lock可以解决这个问题

This effect, where a write in one transaction changes the result of a search query in another transaction, is called a phantom

可串行化

实现方法1: 单线程写。例如Redis

实现方法2: Strong strict two-phase locking(SS2PL)。
InnoDB和SQL Server的可串行化隔离级别就用的该方法实现。这种方法不同于快照隔离要求的对同一条记录的读写不冲突,它是读写互斥

  • 读的时候要加读锁
  • 写的时候要加写锁
  • 如果是先读后写,先持有读锁,写的时候变为写锁
  • 一旦持有锁,事务结束时才会释放
  • 问题:更容易产生死锁,性能比较差
  • 加什么锁:为了避免插入幻影记录,加的锁一般都是index-range locking(或称为next-key locking)

实现方法3:Serializable Snapshot Isolation (SSI)。一种乐观的并发控制技术

  • based on snapshot isolation—that is, all reads within a transaction are made from a consistent snapshot of the database. 基于快照隔离
  • adds an algorithm for detecting serialization conflicts among writes and determining which transactions to abort. 检测冲突,然后决定哪些事务该abort

参考:
Designing Data-Intensive Applications https://book.douban.com/subject/26197294/

posted @ 2022-02-10 18:12  elimsc  阅读(47)  评论(0编辑  收藏  举报