MySQL对自增主键锁做了优化,尽量在申请到自增id以后,就释放自增锁
insert语句是一个很轻量的操作,不过,这个结论对于"普通的insert语句"才有效,也就是说,还有些insert语句是属于特殊情况的,在执行过程中需要给其他资源加锁,或者无法在申请到自增id以后就立马释放自增锁
接下来聊聊这个话题
一、insert … select语句
表t和t2的表结构和初始化数据语句如下:
表结构语句:
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的所有行和间隙加锁呢?
其实,这个问题需要考虑的还是日志和数据的一致性,看看并发insert场景时的执行序列:
实际的执行效果是,如果session B先执行,由于这个语句对表t主键索引加了(-∞,1]
这个next-key lock
,会在语句执行完成后,才允许session A的insert语句执行
但如果没有锁的话,就可能出现session B的insert语句先执行,但是后写入binlog的情况
于是,在binlog_format=statement
的情况下,binlog
里面就记录了这样的语句序列:
insert into t values(-1,-1,-1);
insert into t2(c,d) select c,d from t;
这个语句到了备库执行,就会把id=-1这一行也写到表t2中,出现主备不一致