two-phase locking(2pl)是指在一个事务分成两个阶段:
1. 第1阶段只能加锁
2. 第2阶段只能释放锁
它的反面就是: 对A加锁,然后释放; 再加B锁,再释放。。。
后面这种方式保证不了serializability. 比如,
事务甲 事务乙
———————-
对A加写锁
set A=100
对A释放写锁
对A加写锁
set A=200
对A释放写锁
对A加读锁
对B加写锁
set B=A*2=…
select B
对A释放读锁
对B释放写锁
———————-
事务甲结束后,B的值变成了400; 而我们本来预期这个值应该是200的 (没有满足repeatable read这个级别)
要解决这个问题,可以干脆等事务甲全部结束再执行事务乙;既然事务之间是通过锁来协调的,可以让事务甲的锁全部释放了,再让事务乙获得锁,以保证一致性。
事务甲 事务乙
———————-
对A加写锁
set A=100
对A加写锁
等待。。。。
对B加写锁
set B=A*2=…
select B
对A释放读锁
set A=200
对A释放写锁
对B释放写锁
———————-
事务甲结束后,B的值变成了200,符合预期。这时事务甲用的就是2pl, 释放第一个锁后不再加任何锁。
2pl有个缺点:事务有多久,一个锁就会持续多久,即使你加的是读锁。结合 MVCC机制(Multi-Version Concurrency Control)情况可以好一点,MVCC并没有否定2pl,只不过在读时它并不加锁。