MySQL5.7官⽅⽂档翻译-innodb事务模型与锁
官⽅⽂档:
命令查询记录
查询事务的锁定(可以查看事务存在的意图锁,记录锁)
show engine innodb status
查询当前会话的事务级别
select @@transaction_isolation;
查询全局事务级别
select @@ansaction_isolation;
设置当前会话的事务级别
set session transaction isolation level repeatable read;
锁定读取(只有在禁⽤⾃动提交时才能set autocommit=0)
设置共享模式锁定(给select加读锁)
SELECT ... LOCK IN SHARE MODE改要等待本事务提交。如果其他事务已经修改了要读取的⾏但是没有提交,则本事务等待其他事务结束,并获取最新的值
查询索引锁定(给select加写锁)
SELECT ... FOR UPDATE
搜索到的索引记录,锁定⾏/索引条⽬,阻⽌其他事务更新这些⾏,设置共享模式锁定,或者读取⾏
Shared and Exclusive Locks 共享锁和排他锁
事务T1在row⾏上有共享锁
1、事务T2申请共享锁可⽴即获得,T1, T2同时拥有共享锁
2、事务T2申请排他锁需要等待T1释放
事务T1在row⾏上有排他锁
1、事务T2申请共享锁 or 排他锁,均需等待T1释放
Intention Locks 意图锁(表级锁)
innodb⽀持多粒度锁:即⽀持同时存在⾏锁和标锁。为了⽀持这个机制,有了意图锁
⽬的:表名事务之后需要在⼀给表的某⾏中需要共享 or 排他锁
意图共享锁IS:事务打算在⼀张表的个别⾏申请共享锁(或正拥有)(select ... for share)
意图排他锁IX:事务打算再⼀张表的个别⾏申请排他锁(或正拥有)(select ... for update)
事务需要申请共享⾏锁之前,先要申请表的意图共享锁或意图排他锁
是⽆需要申请排他航所之前,先要申请表的意图排他锁
X IX S IS
X互斥互斥互斥互斥
IX互斥兼容互斥兼容
S互斥互斥兼容兼容
IS互斥兼容兼容兼容
(意图锁之间互相兼容,其余遵循X,S互斥原则)
意图锁不会阻⽌除了完整表请求之外的任何(如何lock tables ... write)
某个事务上的某个意图锁SHOW ENGINE INNODB STATUS:
TABLE LOCK table test.t trx id 10080 lock mode IX
Record Locks 索引记录锁
SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE给c1=10的⾏上排他锁,如果查询⽤的是聚集索引,就会使⽤此排他记录锁
SHOW ENGINE INNODB STATUS
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;
给10~20之间的⾏上排他间隙锁,阻⽌其他事务更新
间隙锁在部分隔离级别下使⽤
在唯⼀索引上的查询(不包括多个col的唯⼀索引,搜索条件为某些列),不使⽤间隙锁。例:SELECT * FROM child WHERE id = 100;只有记录锁,没有间隙锁。如果id没有索引或者不是唯⼀索引,会在100之前的所有⾏上间隙锁
注意:不同的事务可以在⼀个间隙上拥有不同的间隙锁。例如:事务A在⼀个间隙上拥有共享间隙锁,
同时事务B在同⼀间隙上拥有排他间隙锁(⽆论是间隙S锁还是间隙X锁)。这个机制被允许存在是因为,⼀个记录被删除时,会和并不同事务在这个记录上的间隙锁
间隙锁唯⼀的作⽤就是阻⽌其他事务在间隙上的更改,⼀个事务在⼀个记录上有间隙锁不阻⽌其他事务在此间隙上获得间隙锁
在读提交隔离级别上,间隙锁不被使⽤,除⾮在进⾏外键约束检查和重复值检查时。在读提交隔离级别上,不到⾏时,记录锁被释放。Next-Key Locks
这个锁是索引数据上的记录锁和索引数据之前的间隙锁的结合
InnoDB以这样的⽅式执⾏⾏级锁定:当它搜索或扫描表索引时,它会在遇到的索引记录上设置共享锁或排它锁。因此,⾏级锁实际上是索引记录锁。
在索引记录上的next-key lock依然影响在索引记录之前的间隙锁。所以,next-key lock是⼀个索引记录锁+在这个索引数据之前的间隙锁。如果⼀个会话有记录R索引上的共享/排他锁,其他会话不可以插⼊⼀个新的索引记录在这个R索引之前
mysql帮助文档假设10,11,13,20上有索引,可能的next-key lock如下,最后⼀个间隔,next-key lock锁定最⼤索引值之后的间隙
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
innodb在可重复度级别下,使⽤next-key lock⽤于查和索引扫描,防⽌幻读
查看锁状态
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        ;;
插⼊意图锁
插⼊意图锁在执⾏插⼊⾏数据之前的设置的⼀种间隙锁
这个锁表⽰,在想要插⼊的时候,多个事务插⼊共同的索引间隙不需要相互等待,若果他们的⾏在间隙中位置不同
假设,47上有索引记录锁,独⽴的事务想要插⼊5和6,每个事务在得到插⼊⾏上的排他锁之前,使⽤插⼊意图锁锁住47这个间隙。但是每个事务不阻塞彼此
举例:客户端A创建⼀张表有2个索引数据(90和102),开始⼀个事务,放置⼀个排他索引记录锁在ID>100的数据,排他锁也包括⼀个102数据之前的间隙锁
mysql> CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90),(102);
mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
+-----+
| id  |
+-----+
| 102 |
+-----+
客户端B开始⼀个事务插⼊⼀条记录到间隙中,这个事务在等待排他锁的时候持有⼀个插⼊意图锁
mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);
show engine innodb status查看
RECORD LOCKS space id 31 page no 3 n bits 72 index `PRIMARY` of table `test`.`child`
trx id 8731 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 80000066; asc    f;;
1: len 6; hex 000000002215; asc    " ;;
2: len 7; hex 9000000172011c; asc    r  ;;...
AUTO-INC锁定
⼀个AUTO-INC锁是⼀个特殊的表级锁,当事务插⼊表并有⾃增列的时候持有。举例,如果⼀个事务在插⼊数据,其他事务需要等待⾃⼰的插⼊,第⼀个事务将得到连续的主键值
例⼦
客户端A创建⼀个包含两个索引记录(90和102)的表,然后启动⼀个事务,该事务对ID⼤于100的索引记录放置独占锁。独占锁包括记录102之前的间隙锁:
mysql> CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90),(102);
mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。