问题说明
在mysql中造测试数据时,出现了主键不连续的情况
CREATE TABLE test ( id int NOT NULL AUTO_INCREMENT COMMENT 'ID', name varchar(255) , PRIMARY KEY (id)
);insert into test (name) VALUES ('A');insert into test (name) select name from test;
insert into test (name) select name from test;
insert into test (name) select name from test;
insert into test (name) select name from test;select * from test;
结果:
`1 A`
`2 A`
`3 A`
`4 A`
`6 A`
`7 A`
`8 A`
`9 A`
`13 A`
`14 A`
`15 A`
`16 A`
`17 A`
`18 A`
`19 A`
`20 A`
5,10,11,12 丢失了
原因分析
经查询发现是innodb存储引擎有三种自增模式
innodb_autoinc_lock_mode 变量有三种可能的设置:0、1 或 2,分别对应“传统”(traditional)、“连续”(consecutive)和“交错”(interleaved)锁模式。在 MySQL 8.4 中,默认设置为交错锁模式(innodb_autoinc_lock_mode=2)。
SHOW VARIABLES LIKE 'innodb_autoinc_lock_mode';
Variable_name | Value |
---|---|
innodb_autoinc_lock_mode | 2 |
交错锁模式是默认设置。
- innodb_autoinc_lock_mode = 0(“传统”锁模式)
传统锁模式提供了与 innodb_autoinc_lock_mode 变量引入之前相同的行为。传统锁模式是为了向后兼容、性能测试以及解决“混合模式插入”问题(由于语义上的差异)而提供的。在这个锁模式下,所有“插入类”语句会获取一个特殊的表级 AUTO-INC 锁,这个锁通常会保持到语句执行结束(而不是事务结束),以确保自动增长值在一系列插入语句中按照可预测且可重复的顺序分配,并且保证同一语句分配的自动增长值是连续的。
- innodb_autoinc_lock_mode = 1(“连续”锁模式)
在此模式下,“批量插入”语句使用特殊的 AUTO-INC 表级锁,并保持该锁直到语句结束。这适用于所有的 INSERT … SELECT、REPLACE … SELECT 和 LOAD DATA 语句。在一个时间点,只能有一个语句持有 AUTO-INC 锁并执行。如果批量插入操作的源表和目标表不同,目标表上的 AUTO-INC 锁会在对源表的第一行加共享锁后获取。如果源表和目标表相同,AUTO-INC 锁会在对所有选定行加共享锁后获取。此锁模式确保,即使执行了插入语句,其中的自动增长值也是连续的,并且对于基于语句的复制是安全的。
- innodb_autoinc_lock_mode = 2(“交错”锁模式)
在此模式下,没有“插入类”语句使用表级的 AUTO-INC 锁,允许多个语句同时执行。这是最快且最具扩展性的锁模式,但在此模式下,自动增长值保证在所有并发执行的“插入类”语句中唯一且单调递增。然而,由于多个语句可以同时生成自动增长值(即,分配过程交错进行),因此某个语句生成的插入行的值可能不是连续的。
解决办法
主键的不连续通常不影响业务,切换到连续模式在并发场景性能相对较差
切换方法,在配置文件如my.ini
或my.cnf
中增加一行
innodb_autoinc_lock_mode=0
参考
[1] http://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference
[2] https://dev.mysql.com/doc/refman/8.4/en/innodb-auto-increment-handling.html#innodb-auto-increment-lock-modes
[3] https://stackoverflow.com/questions/14641847/how-to-change-innodb-autoinc-lock-mode-to-be-set-to-0-from-1