数据库的多粒度锁

最近在研究数据库的事务和并发控制。其中的多粒度锁,意向锁(intention lock)书上写的比较抽象,不太具体。网上搜的资料又很概括,描述的清的很少,花了一些时间去理解意向锁整个运行的机制。

意向锁要解决的问题

如果数据库需要支持多粒度的锁,比如 innodb 支持表锁、行锁。当有多粒度的锁的时候,加锁和解锁就跟只有一个维度锁的情况很不一样了。比如只有行锁,加锁和解锁只需要检查这行数据当前有没有不相容的锁就行了。解锁更简单,直接解除。在多粒度锁情况下,比如下图:

数据库下分 A1 A2 两个区,每个区下再分文件,文件的下面才是数据行。如果要对某个数据行(叶子节点)加锁,需要事先确认他的父节点们(直到根)是不是已经被加了不相容的锁,如果已经加了就不能授权对这个数据行加锁。如果是一个非叶子节点加锁,需要同时去锁住这个节点的所有子节点。这两种情况如果完全按照上面描述的方法去检查和遍历加锁,性能显然是糟糕的。意向锁是来解决这个问题的(大体的理解,不对以后再更正)。

意向锁是如何解决的

对一个数据行或节点加意向锁表示含义是:当前事务想要在这个节点或者这个节点的子节点加某种锁(共享或者排他)。有了意向锁以后,对任何数据加实际锁(普通的锁用实际锁称呼)都先要先加意向锁再加实际锁。举个对叶子节点数据行加排他锁的例子:

比如事务 T1,要在 Ra1上加排他锁。他需要先去 DB、A1、Fa 加意向排他锁,加了以后才能在 Ra1 上加实际的排他锁。如果实际的锁是加在非叶子节点情况也一样。

先加意向锁有两个作用。一是在当前加锁节点的根路径上的节点都会加上对应的意向锁,这些根路径节点上的意向锁像是一个标记,标记着:这些每个根路径上的节点当前都有一个事务在他们上面或者他们的子节点上可能加了某种锁。如果刚好有其他事务需要加的锁在这些节点的子节点里,这个事务在加实际的锁的时候,就能通过检查想要加锁的节点上是否已经存在的意向锁(前述说的标志),来确定当前事务是不是能加锁。

比如一个事务已经 Ra1 上持有了实际的排他锁,这个时候 Fa、A1、DB 都有一个意向排他锁。如果有另外的事务想在 Fa 上加实际的读锁,已经有标志(Fa 上其他事务的意向排他锁),所以就加不成了,需要等待。

再举个不同的例子,比如一个事务已经在 Fa 上加了实际的排他锁。这时有个新的事务要在 Ra1 上加实际的读锁。按照前述的加锁规则,需要先去父节点加意向读锁,这个时候 Fa 上已经有一个实际的排他锁,在上面就加不成加意向读锁了。为什么加不成呢?既然已经有人在写了(获取了排他锁),就不能再把数据开放给其他想要读的事务了,共享锁和排他锁不相容。

上述两个例子列举了当想加锁的节点,在父节点已经有不兼容锁和在子节点有不兼容锁的情况。

把意向锁的相容性和实际锁放进一个表格比对:

IS IX S SIX X
IS T T T T F
IX T T F F F
S T F T F F
SIX T F F F F
X F F F F F

SIX 是啥待续 …

资料

http://www.edugrabs.com/multiple-granularity/

数据库系统概念