MySQL锁和事务:InnoDB锁(MySQL 官方文档粗翻)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL锁和事务:InnoDB锁(MySQL 官方文档粗翻)相关的知识,希望对你有一定的参考价值。
// 写在前面,实际上,数据库加锁的类型和范围受到多种因素的影响,例如数据库隔离等级,SQL语句,是否使用主键、索引等等。可以查看博文: http://www.cnblogs.com/zhaoyl/p/4121010.html 了解
这一章节讲述了InnoDB使用的锁类型。
- 共享锁(S)和独占锁(X)
- 意向锁
- 行锁(record lock,不知道叫 记录锁是不是更好,百度了一下有人叫行锁)
- 间隙锁(gap lock)
- Next-Key锁
- 插入意向锁
- AUTO-INC(自增长)锁
- 空间锁(Predicate Locks for Spatial Indexes)
共享锁和独占锁
InnoDB实现了两种标准的行级锁,一种是共享锁(S锁),一种是独占锁(X锁)。
- 共享锁允许事务读取一条记录。
- 独占锁允许事务更新或者删除一条记录的锁。
如果一个事务 T1 拥有记录 r 的共享锁,另外一个事务 T2 想要对行 r 加锁的请求会得到这样的处理:
- 如果 T2 想要加一个 S 锁,那么他能马上获得这个锁。结果就是,T1 和 T2 同时拥有 r 的共享锁。
- T2 想要获取的是 X 锁,那么这么请求会被阻塞。
如果一个事务 T1 拥有行 r 的 X 锁,另外一个事务 无论想获取 r 的什么锁都会被阻塞。事务 T2 必须要先等待 T1释放 r 上面的S锁。
意向锁
InnoDB 支持多粒度的锁,允许行锁和表锁共存。为了支持多粒度的锁,InnoDB引入了意向锁。意向锁是一个表级别的锁,表明一个事务在之后(requires later)要获取表中某些行的S锁或X锁。InnoDB中使用了两种意向锁(假设事务 T 已经向表 t 请求获取对应的意向锁)
- 意向共享锁(IS):事务 T 想要对表 t 中的一些行加上S锁。
- 意向排他锁(IX):事务 T 想要对这些行加上X锁。
例如
SELECT ... LOCK IN SHARE MODE
设置了 IS 锁,而
SELECT ... FOR UPDATE
设置了 IX 锁。
意向锁协议(The intention locking protocol)如下所示:
- 在一个事务获取表 t 中某一行的 S锁之前,他必须先获取 IS锁或者一个更重量级的锁(X等)
- 在一个事务获取一行的 X锁之前,他必须先获取表 t的 IX锁。
这些规则可以总结为下面的图表(横向表示一个事务已经获取了对应的锁,纵向表示另外一个锁想要获取对应的锁--这段话我自己加的):
X | IX | S | IS | |
X | 冲突 | 冲突 | 冲突 | 冲突 |
IX | 冲突 | 不冲突 | 冲突 | 不冲突 |
S | 冲突 | 冲突 | 不冲突 | 不冲突 |
IS | 冲突 | 不冲突 | 不冲突 | 不冲突 |
当行上的锁与已经存在的锁不冲突(兼容)时,可以被事务请求获取,如果冲突话,就不行。事务必须等待冲突的锁被释放(才能获取想要的锁)。如果一个加锁请求与已存在的锁相互冲突,又一直得不到锁,可能是发生了死锁,或者出现了错误。
因此,意向锁不会阻塞除全表请求(例如,LOCK TABLES ...WAITE)之外其他请求。IX 和 IS锁的主要目的是表明 某个请求(someone)已经锁定了一行,或者将要锁定一行记录。
意向锁加锁过程中记录的事务数据类似于下面使用 SHOW ENGINE INNODB STATUS语句的 InnoDB 监控输出:
TABLE LOCK TABLE ‘test.‘t‘ trx id 10080 lock mode IX
行锁
行锁是一个加在索引记录上的锁,例如,SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;会阻止其他任何事务插入,更新或者删除那些 t.c1 = 10的记录。
行锁总是在索引记录上面加锁,即使一张表没有任何索引,在这种情况下,InnoDB会创建一个隐藏的聚集索引,然后使用这个索引来加上行锁。
行锁加锁过程中记录的事务数据类似于下面使用 SHOW ENGINE INNODB STATUS语句的 InnoDB 监控输出:
RECORDS 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 on 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 ;;
间隙锁
间隙锁是在索引记录的间隙上加的锁,或者在第一条索引记录之前、最后一条索引记录之后的区间上加的锁。例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 AND 20 FOR UPDATE; 阻止所有其他的事务 插入一条 t.c1 = 15的记录,无论是否表中已经有一列的值是15(或者其他10-20之间的值),因为在这个范围内的间隙都已经被加上了锁。
间隙可能只包括一个索引值,也可能包括多个索引值,甚至不包含任何索引值。
使用唯一索引查找唯一一条记录的语句是不会使用到间隙锁的(这不包括查询条件仅包含一个唯一索引的一部分的语句)。例如,假如 id 列有一个唯一索引,下面的语句只会使用行锁加到 id = 100的行上面,而不会关心别的会话(session)是否在上述的间隙中插入数据。
SELECT * FROM child WHERE id = 100 ; (感觉应该加一个 FOR UPDATE 或者 IN SHARE MODE 或者 数据库隔离等级为 serializable)
如果 id 列没有索引或者不是唯一索引,这个语句会在上述的间隙上加锁。
值得提醒的是,不同的事务可以在间隙上拥有相互冲突的锁。例如,事务 A 可以某一个间隙上的共享锁(gap S-lock),同时另外一个事务可以在同一个间隙上拥有独占锁(gap X-lock)。原因在于如果一条索引记录被删除,其他事务拥有的对应间隙锁必须进行归并(强行翻译,不太明白,原文:The reason conflicting gap locks are allowed is that if a record is purged from an index, the gap locks held on the record by different transactions must be merged.)
间隙锁是 ”完全禁止性的“(强行翻译:purely inhibitive,看下文应该很好理解),意味着他们只会禁止其他的事务插入数据到间隙之中,而不会禁止别的事务获取同一区间的间隙锁。因此,间隙 X锁和间隙 S锁起到的效果是一样的。
间隙锁可以被明确的禁止。你可以将事务隔离等级设置为 READ COMMITTED 或者启用 innodb_locks_unsafe_for_binlog 系统变量(现在不建议使用 deprecated)。在这些情况下,间隙锁在查询和索引扫描中被禁用,只会在外键约束检查和重复索引检查时才会使用。
使用READ COMMITTED 隔离等级和 启用innodb_locks_unsafe_for_binlog系统变量也会造成一些额外的影响。在mysql分析完 WHERE 条件之后,不匹配的行上面的行锁会被释放。对于 UPDATE 语句,InnoDB 进行一个”半一致性“读,它会返回最新提交的版本(的行记录)给MySQL,然后MySQL
确定哪些行匹配了 UPDATE 的 WHERE 条件。
Next-Key Locks
next-key 锁是行锁和在行锁之前的间隙上的间隙锁的联合。
InnoDB以这种形式实现行级锁,当他查找或扫描表索引的时候,遇到匹配的的索引记录,在上面加上对应的共享锁或者独占锁。因此,行级锁实际上是索引记录锁。next-key锁同是会影响索引记录之前的间隙。就是说,next-key lock就是一个索引记录锁加上一个在索引记录之前的间隙上的间隙锁。如果一个会话拥有记录 R 的索引上面的一个共享锁或独占锁,另外的会话无法立即在 R记录索引顺序之前的间隙上插入一条新的记录。
假设有一个索引包含值10,11,13和20。下列的间隔上都可能加上一个next-key lock,(符号表示不包含端点,]符号表示包含端点。
(negative infinity ,10] (10,11] (11,13] (13,20] (20,positive infinity)
在最后一个区间中,next-key lock锁定了索引中的最大值到一个“上确界”(一个虚假的索引记录,他的值比所有索引记录中所有值都要大)。上确界不是一个真实存在的索引记录,所以,事实上,这个nex-key lock只是锁定了最大索引值之后的区间。
默认情况下,InnoDB启用 REPEATABLE READ 事务隔离等级。在这种情况下,InnoDB在查找和扫描索引时会使用 nex-key lock,能避免 幻(影)行的出现。
next-key lock 加锁过程中记录的事务数据类似于下面使用 SHOW ENGINE INNODB STATUS语句的 InnoDB 监控输出:
RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t` trx id 10080 lock_mode X Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 0: len 8; hex 73757072656d756d; asc supremum;; 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 ;;
插入意向锁
插入意向锁
AUTO-INC Locks(自增长锁)
自增长锁是特殊的表级锁,被那些插入带有AUTO_INCREMENT行的记录到表中时占用。一个最简单的例子,当一个事务在插入数据到表中时,其他任何的事务都等待,因此,第一个事务插入的行能够拥有连续的主键值。
innodb_autoinc_lock_mode配置选项控制了自增长锁使用的算法。他允许你在可预测的自增长值和最大化并发插入操作之间进行权衡。
空间索引的断言锁
InnoDB支持空间列上面的空间索引。
在处理包括空间索引的相关锁定操作时,next-key locking 在支持 REPEATABLE READ 和 SERIALIZABLE 事务隔离等级 上工作的不太好。在多维的数据上,没有一个绝对的顺序,因此,“下一个”键在哪里并不是很清楚。
为了支持带有 空间索引的隔离等级,InnoDB使用了断言锁。空间索引包含了 最小外接矩阵(minimum bounding rectangle = MBR)值,所以InnoDB 强制对索引一致性读的时候对查询语句中使用的MBR值上设置一个断言索引。其他的事务不能插入或者修改匹配这个查询条件的行。
以上是关于MySQL锁和事务:InnoDB锁(MySQL 官方文档粗翻)的主要内容,如果未能解决你的问题,请参考以下文章