Skip to content

Latest commit

 

History

History
44 lines (24 loc) · 2.78 KB

File metadata and controls

44 lines (24 loc) · 2.78 KB

5.7 锁

锁是一种解决数据库并发问题的一种设计。

如果一昧的加一种锁,比如全局锁,只要有事务来访问数据,就会申请一个全局锁,虽然从理论上来说,这样是可行的,但毫无疑问,这样的并发管理能力太差,所以需要一些更细粒度化的锁来进行并发控制。

细粒度的锁

将锁更细粒度化可以分为共享锁(share-mode lock;S-lock)和独占锁(exclusive-mode lock;X-lock),也就是读锁和写锁,事务进行读取的时候,获取数据的读锁,其他事务要修改必须要等待解锁。如果事务进行更新操作的时候,需要获取数据的写锁,只有该事务能对该数据进行读写操作。

只有读锁之间可以相互兼容,其他情况都是不兼容。

S-lock X-lock
S-lock 兼容 不兼容
X-lock 不兼容 不兼容

这里通过一个简单的示例来帮助大家理解。

T1:B 转账给 A 50 元

T2:读一下 A 和 B 的余额之和

img

第一眼看上去感觉结果是合理,假设 A 和 B 原来都只有 100 元,事务 T2 无论在 T1 执行前还是执行后进行操作,读取的结果都是 200 元。

img

这样 T2 在 T1 中间执行,锁之间的使用是合理的,这种情况下 T2 的结果就会是 150 元。

那怎么处理才能解决上面这种情况呢,很容易想到的就是事务在最后的时刻释放读写锁,这样看上去也很合理,但会出现下面这种情况:

img

这种情况下,T3 占着 B 的写锁,T4 占着 A 的读锁,这个时候 T3 去申请 A 的写锁,T4 申请 B 的读锁,很明显,这种情况下死锁了。

因此为了避免这种情况,我们引入了两阶段加锁,在获取锁的阶段(growing phase),事务只能不断地获取新锁,在释放锁(shrinking phase)阶段,事务只能不断的释放锁。

两阶段加锁

img

但同样也会遇到上述的情况,在 T5 遇到错误的时候,会导致 T6 和 T7 的连锁回滚。为了避免连锁回滚,我们引入更严格的两阶段加锁,写锁必须等事务结束后释放,这样就避免在事务未提交的时候,其他事务不会来进行数据改写,因此避免了连锁回滚。