InnoDB锁类型
InnoDB引擎使用了八种类型的锁,他们分别是:
- 共享排他锁(Shared and Exclusive Locks)
- 意向锁(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 实现了标准的行级锁并且他有两种锁的类型,共享锁(S)和排它锁(X):
- 共享锁允许持有锁的事物读取一行数据
- 排它锁允许持有锁的事物删除或者更新一行数据
如果事物T1持有r行共享锁(S),那么事物T2希望对r行上锁的处理如下:
- T2请求一个S锁,会立即准许。结果,T1和T2两个都会持有S锁。
- T2请求一个X锁,不会立即准许。(猜测可能会等待)
如果事物T1对r行持有排它锁(X),另一个事物T2的请求则不能够被立即准许持有r行任何一种类型的锁。相反,事物T2不得不等待事物T1释放r行锁。
意向锁(Intention Locks)
InnoDB支持多粒度锁,允许表锁和行锁共存。例如,就像一个表级锁/写( LOCK TABLES ... WRITE)也会带有一个排它锁(X)[释义:排它锁属于行级
]。为了实现多力度级别锁,InnoDB使用了意向锁(Intention Locks)。意向锁是表级别的锁,它表明事务稍后对表中的行需要哪种类型的锁(共享或独占)。意向锁有两种类型:
- 意向共享锁(IS)表明一个事物打算在表的单个行设置共享锁。
- 意向排它锁(IX)表明一个事物打算在表的单个行设置排它锁。
例如,执行SELECT ... LOCK IN SHARE MODE
的时候设置一个IS锁,和执行 SELECT ... FOR UPDATE
的时候设置IX锁。
这个意向锁的协议如下:
一个事物能够在表中获取行级共享锁之前,它必须首先在一个表中获得一个IS锁或者更强类型的锁。
一个事物能够在表中获取行级排它锁之前,它必须首先在一个表中获得一个IX锁。
表级别锁类型的兼容性总结如下表所示:
排它锁 | 意向排它锁 | 共享锁 | 意向共享锁 | |
---|---|---|---|---|
排它锁 | x | x | x | x |
意向排它锁 | x | y | x | y |
共享锁 | x | x | y | y |
意向共享锁 | x | y | y | y |
如果一个事物请求锁(A)与已存在的锁(B)兼容,则该事物可以得到锁(A);但是,如果不兼容则该事物不会得到锁(A)。事物(A)会一直等待直到锁(B)被释放为止。如果锁(A)请求与已存在锁(B)冲突,则该请求将不能够得到锁(A),因为它可能引发死锁或错误。
除了全表请求以外意向锁不会被阻塞(例如:LOCK TABLES ... WRITE
),意向锁最主要的目的是显示某人锁住一行数据或者打算锁住表中的一行数据。
在InnoDB引擎显示和监控输出中意向锁的事物数据如下所示:
TABLE LOCK table `test`.`t` trx id 10080 lock mode IX
记录锁(Record Locks)
记录锁是索引记录上的锁。例如:SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE
;预防任何事物从t.c1 = 10.
的行插入,更新,和删除额操作。
记录锁总是锁定索引记录,即使表中没有定义索引。对于这种情况,InnoDB 创建了隐式的聚簇索引并使用它来锁定记录。参考:聚簇索引和二级索引
在InnoDB引擎的显示和监控输出中记录锁的事物数据如下所示:
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;
防止其他事物能够从t.c1
列插入值为15的数据,不论列中是否已经存在这样的记录值,因为该范围内所有已经存在的值都已被锁定。
间隙可能跨越单个索引,多个索引,甚至是空索引。
隙锁是权衡了性能和并发能力的一个折中选择,在某些隔离级别中使用了间隙锁。
使用唯一索引搜索唯一行时,不需要使用间隙锁。(这并不包括在查找条件中,条件列只是多列唯一索引中的一部分时;此时,间隙锁定会发生)。例如,如果id列有一个惟一的索引,下面的语句只对id值为100的行使用索引记录锁,其他会话是否在前面的空格中插入行并不重要:
SELECT * FROM child WHERE id = 100;
如果id不是索引,或者不是一个唯一索引,则这条语句就会锁住之前的间隙。
在这里值得注意的是,不同的事物可以在间隙上持有冲突锁。例如:事物A在间隙上持有一个共享间隙锁 (gap S-lock) ,同时事物B在相同的间隙上也持有一个排它锁(gap X-lock) 。允许间隙锁冲突的原因是,如果从索引中清除一条记录,则在不同事物中持有的间隙锁记录必须合并。
间隙锁在InnoDB中“完全被抑制的”,这意味着它们的唯一目的是阻止其它事物插入此间隙。间隙锁可以共存。一个事物所采取的间隙锁并不能够阻止其他事物采取相同间隙的间隙锁。共享间隙锁和排它间隙锁是相同。它们之间是不冲突的,并且执行相同的功能。
如果改变事物隔离级别为READ COMMITTED
或者启用系统变量innodb_locks_unsafe_for_binlog
就可以明确的禁用间隙锁。在这种情况下,对搜索和索引扫描禁用间隙锁,只用于外键约束检查和重复键检查。
使用 READ COMMITTED
隔离级别或启用 innodb_locks_unsafe_for_binlog
还有其他影响。 MySQL评估 WHERE
条件后,将释放非匹配行的记录锁。对于UPDATE
语句,InnoDB 执行“半一致(semi-consistent)
”读取,以便将最新提交的版本返回给MySQL,以便MySQL可以确定该行是否与 UPDATE
的WHERE
条件匹配。
临键锁 (Next-Key Locks)
未完待续
插入意图锁 (Insert Intention Locks)
未完待续
自增锁 (AUTO-INC Locks)
未完待续
空间锁和预测锁(Predicate Locks for Spatial Indexes)
未完待续
声明
-
原文地址:
https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html -
本文只做学习参考,如有任何不准确的地方欢迎指正。(翻译的不好请见谅!)
-
我的邮箱:
-lulongji2011@163.com
网友评论