InnoDB的锁

在InnoDB中主要有以下几类锁:

  • 共享锁(shared lock)和排他锁(exclusive lock)
  • 意向锁(intention locks)
  • 行锁(record locks)
  • 间隙锁(gap locks)
  • next-key locks
  • 插入意向锁(insert intention locks)
  • 自增长锁(auto-inc locks)
  • Predicate Locks for Spatial Indexes

Shared and Exclusive Locks

Innodb支持标准的行级别锁,有两种类型,分别为共享锁和排他锁

  • 共享锁允许持有锁的事务读取行数据;
  • 拍它锁允许持有锁的事务修改或者删除数据。

如果事务T1持有在行 r 上持有共享锁,其他独立的事务 T2 在行 r 上加锁满足下面的条件:

  • 允许其他事务T2 在行r 上加共享锁,这时候,T1和T2可以同时在行r 上持有共享锁
  • 如果T2要在行r 上加拍它锁则不被允许。

如果事务T1持有在行 r 上的排他锁,则其他别的事务T2无论是想要在行 r 上加共享锁还是排他锁都不被允许,T2必须等待T1释放对行 r持有的锁。

Intention Locks

Innodb 支持多种粒度的锁定,允许行锁和表锁共存。比如使用像 LOCK TABLES ... WRITE 这样的SQL在指定的表上面加排他锁。为了在多个粒度上实现锁定,Innodb使用了意向锁(intention locks)。意向锁是一个表级别的锁,指示事务稍后会对表中的行使用哪种类型的锁(共享锁或者排他锁),有两种类型的意向锁:

  • 共享意向锁(intention shared lock) 指明事务打算在表中的个别行上使用共享锁;
  • 排他意向锁(intentive exclusive lock)指明事务打算在表中的个别行上使用排他锁。

比如 SELECT ... LOCK IN SHARE MODE 这个SQL使用了一个共享意向锁,SELECT ... FOR UPDATE 这个SQL使用了一个排他意向锁。

意向锁的协议如下:

  • 事务在获取表中的行的共享锁前首先需要在表上获取共享意向锁或者更强的锁(排他锁);
  • 事务在获取表中的行的排他锁之前首先需要在表上获取排他意向锁。

意向锁并不会阻塞除全表锁之外的任何请求(比如 LOCK TABLE ... WRITE). 他的主要目的是显示某人锁定了一行或者将要锁定表中的一行。

意图锁在 SHOW ENGINE INNODB STATUS 或者 innodb monitor中的输出类型下面这样

TABLE LOCK table `test`.`t` trx id 10080 lock mode IX

行锁(Record Locks)

行锁是在索引上的锁。比例 SELECT c1 FROM table WHERE c1=10 FOR UPDATE;阻止任何其他事务插入,删除或者修改 c1=10的记录。

行锁始终锁的是索引中的记录,即使表中没有定于索引,这种情况InnoDB会隐式创建聚簇索引,然后使用这个聚簇索引来进行行锁。

行锁的事务数据在 SHOW ENGINE INNODB STATUS 和InnoDB monitor的输出中类型如下:

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t` 
trx id 10078 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 00000000274f; asc     'O;;
 2: len 7; hex b60000019d0110; asc        ;;

间隙锁(Gap Locks)

间隙锁是对索引之间的间隙的锁,或者对索引记录第一条之前或索引记录最后一条之后的间隙的锁。比如 SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;会阻止其他事务插入一条c1=15的记录到表中,无论是否存中这样的值,因为间隙内所有范围的值都锁定了。

间隙可能跨越单个索引值,多个索引值,或者是空的。

间隙锁是性能和并发之间的权衡,在某些事务隔离级别,而不是全部。

对于使用唯一索引并搜索唯一行的SQL语句不需要使用间隙索引(这并不包括搜索条件只包含一个符合唯一索引的其中某些列的情况,这种情况还是会使用间隙索引)。比如,有一个 id 列,上面有唯一索引,下面这条SQL只会使用行锁来锁定 id=100的这行记录,其他的会话是否在在前面的间隙中插入行并不重要

SELECT * FROM child WHERE id = 100;

如果 id 列没有索引或者索引不是唯一索引,那么这个SQL将会锁定前面的空隙

值得注意的是不同的事务可以在一个间隙上 持有冲突锁。比如,在事务B持有排他间隙锁的同时事务A可以持有一个共享的间隙锁,允许存冲突的间隙锁的原因是,如果从索引中清除一条记录,则必须合并不同事务在记录中持有的间隙锁。

共享锁在InnoDB中是“纯抑制的”,这意味这他们的唯一目的是防止其他事务插入数据到间隙中,间隙锁是可以共存的。一个事务采取的间隙锁并不阻碍另外一个事务对同一个间隙采取间隙锁,共享间隙锁和排他间隙锁是没有什么不同的。他们都不会与其他事务冲突,他们具有相同的功能。

可以显式的禁用间隙锁。当设置事务隔离级别为 read_commited 或者启用 innodb_locks_unsafe_for_binlog(现在已经被不弃用) 这个系统参数就可以禁用间隙锁。在这种情况下,搜索和索引扫描禁用间隙锁,间隙锁只用于外键约束和重复键的检查。

使用 READ_COMMITED隔离级别或者启用 innodb_locks_unsafe_for_binlog 还有其他的影响。非匹配行的行锁在Mysql评估WHERE条件之后释放。对于 UPDATE 语句,InnoDB执行“半一致”读,这样,它将返回最后被提交的数据给MySQL,以便MySQL确定该行是否匹配 UPDATE 的where 条件.

Next-Key Locks (不知道该翻译成什么名字)

next-key lock 锁是一个组合锁(索引上的行锁和索引记录前的间隙上的间隙锁)

InnoDB在搜索或者扫描表索引时执行行级锁定,它对遇到的索引记录设置共享锁或者排他锁,因此,行锁实际上是索引记录锁。索引记录上的 next-key lock 也会影响这条记录之前的间隙,也就是说 next-key lock 是索引记录锁加上索引记录之前的间隙的间隙锁。

默认情况 InnoDB运行在  REPEATABLE READ事务隔离级别,在这种情况,InnoDB使用 next-key lock用于搜索和索引扫描,防止虚行。

插入意向锁( Insert Intention Lock)

插入意向锁是在插入操作执行前设置的一种间隙锁。如果插入到同一个索引间隙的多个事务不在间隙中的相同位置插入,则此锁表示插入的意图,从而使插入到同一间隙锁的多个事务无需相互等待。假设有值为4和7的索引记录,两个独立的事务分别试图插入5和6的记录,在获得插入行的独占锁之前,每个锁在4和7之间使用插入意向锁的间隙,但不会相互阻塞,因为这些行是不冲突的。

自增长锁(Auto-Inc locks)

自增长锁是一种特殊的表级锁,用于事务插入有自增长列的表,最简单的情况,一个事务正在插入表,那么任何其他事务都必须等待自己插入该表,以便第一个事务插入的行接收连续的主键值。

参数 innodb_autoinc_lock_mode 可以控制使用自增锁的算法,它允许你在严格的有序增长和最大并发插入性能之间进行权衡。

Predicate Locks for spatial Indexs(针对空间索引的锁)

InnoDB支持包含空间列的空间索引

要处理涉及空间索引的锁操作,next-key lock 不能很好的支持 REPEATABLE_READ 和 SERIALIZABLE 事务隔离级别,多维数据中没有绝对的排序概念,因此不清楚哪个键是“下一个”。

为了支持具有空间索引的表隔离级别,InnoDB使用 Predicate Lock,空间索引包含最小边界矩形(MBR)值,因此Innodb通过对与查询的MBR值设置Predicate lock来强制执行对索引数据的一致读,其他事务不能插入或者修改与匹配相关的行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值