Innodb采用乐观插入的方式,所以在做insert操作时不会进行显示加锁,也就是不会生成lock_t锁结构,这样可以极大的减少锁开销,提升整体的性能。
如果没有显示的行锁,该如何保证事务插入的正确性呢?比如说如下两个事务,插入相同的两个主键数据,如下:
create table test(id int not null auto_increment primary key,name varchar(10));
session1 | session2 |
---|---|
begin; | begin; |
insert into test values(1,’test’); | |
insert into test values(1,’test’); |
在事务1提交之前,事务2是不会报错主键冲突的,会陷入等待的状态,那么这种阻塞是如何形成的,将会通过这篇文章进行详细介绍。
Innodb的行锁结构是基于行数据的,而隐式锁没有真正的行锁结构,它通过每一行数据的隐藏字段来完成。Innodb主键索引的每一行数据包含两个隐藏列,其中一个是trx_id,另外一个是回滚段指针。而主键索引上的隐式锁就是通过trx_id来进行设置的,二级索引上的行数据没有trx_id,但是每一个数据页上包含
一个max_trx_id;
在开始研究Insert加锁流程时,一直没看出来是如何操作的,因为在进入加锁代码逻辑中,没有看到设置行数据trx_id的过程。这个过程其实实在之前完成的,在构建Innondb 行数据的时候,就已经把trx_id设置进去了。
对于主键而言,隐式锁的判断只需要查看当前行数据的隐藏列 trx_id是否是活跃事务即可。
对于二级索引会比较麻烦
只有在特殊情况下,才会发生隐式锁到显示锁的转换。这个转换动作并不是加隐式锁的线程自发去做的,而是其他存在行数据冲突的线程去做的。
还是看上面的场景:
表结构
create table t1(id int not null auto_increment primary key,name varchar(10))
操作序号 | session-1 | session-2 |
---|---|---|
1 | begin; | begin; |
2 | insert into t1(id,name) values(1,’aaa’); | |
3 | insert into t2(id,name) values(1,’aaa’); |
操作2时,s1对行数据(1,’aaa’)加隐式锁,不需要lock_t结构,不需要添加到lock hash table中。show engine innodb status
结果如下
------------
TRANSACTIONS
------------
Trx id counter 329052
Purge done for trx's n:o < 329051 undo n:o < 0 state: running but idle
History list length 117
Total number of lock structs in row lock hash table 0 /*lock hash table中没有记录*/
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 281479605203232, not started
0 lock struct(s), heap size 1160, 0 row lock(s)
---TRANSACTION 281479605201056, not started
0 lock struct(s), heap size 1160, 0 row lock(s)
---TRANSACTION 329051, ACTIVE 6 sec
1 lock struct(s), heap size 1160, 0 row lock(s), undo log entries 1
MySQL thread id 3, OS thread handle 123145543389184, query id 87 localhost root
操作3时
锁日志如下:
第一,trx_id=329051的事务,也就是第一条插入语句,增加了一个行锁,并且插入到lock hash table中。
2018-09-12T14:45:33.715550+08:00 5 [Note] InnoDB: trx_id: 329051 create a record lock and add it to lock hash table,
space_id: 195
page_no: 3
heap_no: 2
n_bits: 72
primary key: 1
is record lock: 1
is waiting: 0
is gap: 0
is record not gap: 1
is insert intention: 0
lock_mode: 3 (0:LOCK_IS, 1:LOCK_IX, 2:LOCK_S, 3:LOCK_X, 4:LOCK_AUTO_INC, 5:LOCK_NONE)
第二,trx=329052的事务,也加了一把锁(heap_no: 2,第一条用户数据;间隙锁,共享)
2018-09-12T14:45:33.723318+08:00 5 [Note] InnoDB: trx_id: 329052 create a record lock and add it to lock hash table,
space_id: 195
page_no: 3
heap_no: 2
n_bits: 72
primary key: 1
is record lock: 1
is waiting: 1
is gap: 0
is record not gap: 0
is insert intention: 0
lock_mode: 2 (0:LOCK_IS, 1:LOCK_IX, 2:LOCK_S, 3:LOCK_X, 4:LOCK_AUTO_INC, 5:LOCK_NONE)
第一个锁结构就是由隐式锁转换而来的,而第二把锁处于waiting状态,因为XS冲突导致的。造成如上这种等待的逻辑:
关于这部分的优化,可以转到https://yq.aliyun.com/articles/41123。
mysql-5.7对隐式锁转换的优化
https://yq.aliyun.com/articles/41123
Innodb insert操作的大概流程
https://blog.csdn.net/weixin_40581617/article/details/80623276
隐式锁转换的加锁原理图
http://blog.itpub.net/31493717/viewspace-2150833/