InnoDB存储引擎之锁的实现

InnoDB锁

在一般的数据库中为了保证数据库事务的四个特性,不可避免的要使用到锁.而InnoDB为我们提供了一个很好的行级锁,即锁定的是数据行,而不是整张表.,当然了,在某些情况下也会直接锁表,而不是行锁.下面来探讨一下InnoDB的行级锁.

锁的类型

InnoDB存储引擎为我们提供了两种标准的行级锁.

  • 共享锁(S Lock),允许事务读取一行数据
  • 排它锁(X Lock),允许事务删除或更新一条数据.

正如名称所描述的,共享锁就是能够共享的,就是说在一个事务获取了共享锁之后,另一个事务依旧可以获取共享锁.排它锁是不共享的,当有一个事务获取排它锁之后,则不允许其他任何事务获取任何锁. 在InnoDB中如果想要读取或者写入一条数据,首先应该获得它的锁.

并且,InnoDB存储引擎支持多粒度锁定,这种锁定允许事务在行级上的锁和表级上的锁同时存在. 为了实现这个操作,InnoDB提供了一种额外的锁的方式,称为意向锁.

在InnoDB中,可以将上锁的对象看成一颗树,如果想要对最下层的对象加锁,也就是行锁,就必须要先对上层的对象上锁.也就是说,如果想要在一行上加写锁,首先要在表上加意向排它锁,其次在行所在的页上也加入意向排它锁,最后在那一行上加入排它锁.

这一要注意以下,意向锁本身互相都是相互兼容的.意思就是如果表上加了意向共享锁,那么也可以继续在表上加入意向排它锁.

那这个意向锁又是用来干什么的呢. 意向锁主要是为了很容易的知道我即将要加锁的页或者表能不能被我加锁. 比如说,事务T1为表A中的一行记录加了排他锁,这个时候事务T2要做全表扫描,也就是说要在表级别加共享锁,这个时候,T2首先要判断这个表上是否能加共享锁.如果没有意向锁,那么T2就必须扫描全表,看是否能够加意向共享锁,也就是看看表中有没有任意一行加了排它锁,如果加了,就阻塞,等待,如果任意一行都没有排它锁,则可以加共享锁. 但是有了意向锁之后,因为如果要锁定某一行,就必须先在行锁的页和表上加意向锁.这个时候T2事务只需要看看表上是否有意向排他锁,如果有,则说明,表中有行记录被排它锁锁上了,阻塞,如果没有意向排它锁和排它锁,则自己就可以加锁.

锁的算法

对于行锁有三种算法,分别是:

  • Record Lock: 单个行记录上的锁
  • Gap Lock :间隙锁,锁定一个范围,但不包含本身
  • Next-Key Lock : Gap Lock+ Recork Lock,锁定一个范围,并且锁定记录本身

Record Lock

Record Lock总是会锁住索引记录,如果InnoDB存储引擎在建立标的时候没有设置索引,这个时候会用隐式的主键来锁定.

Gap Lock

Gap Lock锁主要是用来锁住间隙的.也就是为了阻止多个事务将记录插入到同一范围内,这会导致幻读问题的产生.

Next-Key Lock

Next-Key Lock是结合了Gap Lock 和Record Lock的一种锁定算法.这种算法锁定的并不是单个值,而是一个范围.这个锁的出现也是为了防止幻读的出现. 比如:现在有一个索引: 10,11,13,20四个值.
那么索引可能被Next-Key Locking的区间为:

(-无穷,10),(10,11],(11,13],(13,20],(20,+无穷].

通过这种锁,在自己锁定记录行的时候,还会锁住行记录之前的间隙. 分以下几种情况:

  • 对于主键索引: Next-Key Lock会自动降级为Record Lock,单单锁住一个行记录.这里主要是为了提高并发性,
  • 对于唯一辅助索引: 同主键索引一样.会自动降级为Record Lock锁
  • 对于普通的辅助索引: 会首先加上Next-KeyLock锁,并且在行记录后的空隙加上gap Lock锁.