在上一篇文章中,我提到MySQL对自增主键锁做了优化,尽量在申请到自增id以后,就释放自增锁。
因此,insert语句是一个很轻量的操作。不过,这个结论是对于“普通的insert语句”才有效。也就是说,还有些insert语句是属于“特殊情况”的,在执行过程中需要给其他资源加锁,或者无法在申请到自增id以后就立马释放自增锁。
那么,今天这篇文章,我们就一起来聊聊这个话题。
1. insert...select语句
我们先从上一篇文章的话题说起吧。表t1和t2的表结构、初始化数据语句如下,今天的例子我们还是针对这两个表展开。
1 | CREATE TABLE `t` ( |
现在,我们一起来看看为什么在可重复读隔离级别下,binlog_format=statement时执行:
1 | insert iinto t2(c, d) select c, d from t; |
这个语句时需要对表t的所有行和间隙加锁呢?
其实,这个问题我们需要考虑的还是日志和数据的一致性。我们看下这个执行序列:
实际的执行效果是,如果session B先执行,由于这个语句对表t主键索引加了(-∞,1]这个next-key lock,会在语句执行完成后,才允许session A的insert语句执行。
但如果没有锁的话,就可能出现session B的insert语句先执行,但是后写入binlog的情况。于是,在binlog_format=statement的情况下,binlog里面就记录了这样的语句序列:
1 | insert into t values(-1, -1, -1); |
这个语句到了备库执行,就会把id=-1这一行也写到表t2中,出现主备不一致。
2. insert循环写入
当然了,之心吧insert...select的时候,对目标表也不是锁全表,而是只锁住需要访问的资源。
如果现在有这么一个需求:要往表t2中插入一行数据,这一行的c值是表t中c值的最大值加1。
此时,我们可以这么写这条SQL语句:
1 | insert into |