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 官方文档粗翻)的主要内容,如果未能解决你的问题,请参考以下文章

MySQL锁和事务:InnoDB锁(MySQL 官方文档粗翻)

mysql之innodb引擎的行锁和表锁

mysql之innodb引擎的行锁和表锁

Mysql 锁机制和事务

Mysql中那些锁机制之InnoDB

(转)mysql 中的 latch锁和Tlock(事务锁), DML加锁规则,以及死锁分析