insert语句本来是一个轻量级的操作,但是这个语句只是对普通的insert语句有效,也就是说,有些insert语句属于 “特殊情况”

这样在执行过程中需要对其他资源加锁,或者无法申请到id后立刻释放锁

1.

CREATE TABLE `t` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`c` int(11) DEFAULT NULL,

`d` int(11) DEFAULT NULL,

PRIMARY KEY (`id`),

UNIQUE KEY `c` (`c`)

) ENGINE=InnoDB;

insert into t values(null, 1,1);

insert into t values(null, 2,2);

insert into t values(null, 3,3);

insert into t values(null, 4,4);

create table t2 like t

创建完成了这个表,接下来看binlog_format=statement执行

insert into t2(c,d) select c,d from t;

这个语句是否是需要表t的所有行和间隙加锁呢?

接下来执行如下的语句

图片

在真实情况下,两个session并不能一起执行

实际上,如果sessionB先执行了,由于这个语句对表t主键索引加了(-∞,1],这个next-key-lock,会在语句执行完成后,才允许sessionA的insert执行

如果不加锁的话,直接录入binlog的话,可能sessionB的语句先执行,但是在sessionA的语句后写入binlog了

可能出现binlog如下语句

insert into t values(-1,-1,-1);

insert into t2(c,d) select c,d from t;

导致出备库执行的时候,出现了主备不一致的问题

insert….select的语句

在执行insert…select的时候,如果是如下的语句,执行从和一个表上复制到另一个表上

执行如下的语句

insert into t2(c,d) (select c+1,d from t froce index(c) order by  c desc limit 1)

整个加锁范围,就是表t 索引上(3,4]和(4,supermum]两个next key lock

其执行流程很简单,就是从表t按照索引倒序,扫描第一行,拿到结果写入t2中

整个语句的扫描行数是1

但是如果执行如下的语句

insert into t(c,d) (select c+1,d from t force index(c) order by c desc limit 1);

这时候会在explain中发现,这个语句会扫描5行,

图片

在Extra字段中,可以看出Using temporary字段,说明使用了临时表

而且并不是只扫描了1行,而是对表t做了一次全表扫描,整体流程如下:

1.创建临时表,表中有两个字段c,d

2.按照索引c扫描t,取出 c=4,3,2,1 回表取回c d的值放入临时表

3.从临时表中取出第一行,插入表t中,这一共,5行

为什么会出现这个情况,是因为这个语句执行过程中,如果遍历了数据,然后更新数据的话,读出来的数据直接写回原表,可能导致读出来的值是新插入的值,导致语句混淆

这个语句会导致在表 t 上做全表扫描,并且会给索引 c 上的所有间隙都加上共享的 next-key lock。所以,这个语句执行期间,其他事务不能在这个表上插入数据。

2.进行insert…select的时候发生唯一键冲突

图片

在可重复读隔离级别下,可以看出sessionB的insert语句进入了锁等待的状态

也就是说sessionA执行的insert语句,发生唯一键冲突的时候,并不会简单的报错,而是在冲突的索引上加上了锁,一个next key lock是由其右边界的值决定的,这就是加上了(5,10]这个读锁

这个加锁的规则,不仅仅对唯一索引有用,对主键索引同样有用

或者插入带有唯一键的表,也会导致

图片

执行如上的语句流程,在sessionA中,先加入了next key lock(0,5],但是因为是唯一索引,退化为了记录锁,已经加上了写锁,但是因为事务没有提交,迟迟不放手

在发生主键冲突的时候,不会由next-key lock退化为行锁的

session B,C 为了保证一行数据的存在,于是对于加上了读锁,虽然没有加上读锁,但是加上了gap lock

在T3时刻,sessionA回滚了,SessionB 和C,继续进行插入,都需要加上写锁,但需要等待对方的行锁,于是导致死锁

图片

insert into …on duplicate key update

这个例子是主键冲突后报错,可以改写为下面的语句,使用上on duplicate key update语法

这个语法是插入一行数据,如果遇到了唯一键约束,就执行后面的更新

那么执行如下的语句

insert into t values(2,1,10) on duplicate key update d=100;

现在表t1 t2中已经有(1,1,1)(2,2,2)两行数据了

这个语句的执行为

图片

MySQL先判断了id这一个主键索引,然后修改了id=2的行

但是MySQL中,执行这个语句返回的affected rows是2,看起来更新了两行,实际上只更新了一行

今日的总结,介绍了几种特殊情况下的insert语句

insert…select语句,会给扫描到的记录加上间隙锁和读锁

insert语句出现唯一键冲突,会给冲突的唯一值上,加上共享的next-key-lock

发表评论

邮箱地址不会被公开。 必填项已用*标注