MySQL中auto_increment字段估计大家都经常用到,特别是innodb引擎。我也经常用,只知道mysql可以保证这个字段在多进程操作时的原子性,具体原理不甚了了,一次心血来潮,遂去查阅了MySQL手册以及相关资料,了解了个大概。本文只探究mysql5.5中innodb引擎auto_increment的问题,因为myisam引擎不会存在auto_increment空洞问题。

1.传统auto_increment原理

传统的auto_increment实现机制:mysql innodb引擎的表中的auto_increment字段是通过在内存中维护一个auto-increment计数器,来实现该字段的赋值,注意自增字段必须是索引,而且是索引的第一列,不一定要是主键。例如我现在在我的数据库test中创建一个表t,语句如下:

CREATE TABLE t (a bigint unsigned auto_increment primary key) ENGINE=InnoDB;

则字段a为auto_increment类型,在mysql服务器启动后,第一次插入数据到表t时,InnoDB引擎会执行等价于下面的语句:

SELECT MAX(a) FROM t FOR UPDATE;

Innodb获取到当前表中a字段的最大值并将增加1(默认是增加1,如果要调整为增加其他数目,可以设置auto_increment_increment这个配置的设置)然后赋值给该列以及内存中该表对应的计数器。

如果表t为空,则InnoDB用来设置的值为为1.当然这个默认值夜可以通过 auto_increment_offset这个配置项来修改。

auto-increment计数器初始化以后,如果插入数据没有指定auto_increment列的值,则Innodb直接增加auto-increment计数器的值并将增加后的值赋给新的列。如果插入数据指定了auto_increment列的值且这个值大于该表当前计数器的值,则该表计数器的值会被设置为该值。

插入数据时如果指定auto_increment列的值为NULL或者0,则和你没有指定这个列的值一样,mysql会从计数器中分配一个值给该列.而如果指定auto_increment列的值为负数或者超过该列所能存储的最大数值,则该行为在mysql中没有定义,可能会出现问题.根据我的测试来看,插入负值会有警告,不过最终存储的数据还是正确的.如果是超过了比如上面定义的表t的bigint类型的最大值,同样会有警告,而且插入的数值是bigint类型所能存储的最大值18446744073709551615.

在传统的auto_increment设置中,每次访问auto-increment计数器的时候, INNODB都会加上一个名为AUTO-INC锁直到该语句结束(注意锁只持有到语句结束,不是事务结束).AUTO-INC锁是一个特殊的表级别的锁,用来提升包含auto_increment列的并发插入性能.因此,两个事务不能同时获取同一个表上面的AUTO-INC锁,如果持有AUTO-INC锁太长时间可能会影响到数据库性能(比如INSERT INTO t1... SELECT ... FROM t2这类语句).

2.改进的auto_increment

鉴于传统auto_increment机制要加AUTO-INC这种特殊的表级锁,性能还是太差,于是在mysql5.1开始,新增加了一个配置项innodb_autoinc_lock_mode来设定auto_increment方式.可以设置的值为0,1,2.其中0就是第一节中描述的传统auto_increment机制,而1和2则是新增加的模式,默认该值为1,可以中mysql配置文件中修改该值.这里主要来看看这两种新的方式的差别,在描述差别前需要先明确几个插入类型:

1)simple inserts

simple inserts指的是那种能够事先确定插入行数的语句,比如INSERT/REPLACE INTO 等插入单行或者多行的语句,语句中不包括嵌套子查询。此外,INSERT INTO ... ON DUPLICATE KEY UPDATE这类语句也要除外。

2)bulk inserts

bulk inserts指的是事先无法确定插入行数的语句,比如INSERT/REPLACE INTO ... SELECT, LOAD DATA等。

3)mixed-mode inserts

指的是simple inserts类型中有些行指定了auto_increment列的值有些没有指定,比如:

INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');

另外一种mixed-mode inserts是 INSERT ... ON DUPLICATE KEY UPDATE这种语句,可能导致分配的auto_increment值没有被使用。

下面看看设置innodb_autoinc_lock_mode为不同值时的情况:

innodb_autoinc_lock_mode=0(traditional lock mode)

传统的auto_increment机制,详见1.这种模式下所有针对auto_increment列的插入操作都会加AUTO-INC锁,分配的值也是一个个分配,是连续的,正常情况下也不会有空洞(当然如果事务rollback了这个auto_increment值就会浪费掉,从而造成空洞)。

innodb_autoinc_lock_mode=1(consecutive lock mode)

这种情况下,针对bulk inserts才会采用AUTO-INC锁这种方式,而针对simple inserts,则采用了一种新的轻量级的互斥锁来分配auto_increment列的值。当然,如果其他事务已经持有了AUTO-INC锁,则simple inserts需要等待.

需要注意的是,在innodb_autoinc_lock_mode=1时,语句之间是可能出现auto_increment值的间隔的。比如mixed-mode inserts以及bulk inserts中都有可能导致一些分配的auto_increment值被浪费掉从而导致空洞。后面会有例子。

innodb_autoinc_lock_mode=2(interleaved lock mode)

这种模式下任何类型的inserts都不会采用AUTO-INC锁,性能最好,但是在同一条语句内部产生auto_increment值空洞。此外,这种模式对statement-based replication也不安全。

3.可能产生空洞原因总结

经过上面的文档分析,下面总结下针对auto_increment字段的各种类型的inserts语句可能出现空洞问题的原因:

simple inserts

针对innodb_autoinc_lock_mode=0,1,2,只有在一个有auto_increment列操作的事务出现回滚时,分配的auto_increment的值会丢弃不再使用,从而造成空洞。

bulk inserts(这里就不考虑事务回滚的情况了,事务回滚是会造成空洞的)

innodb_autoinc_lock_mode=0,由于一直会持有AUTO-INC锁直到语句结束,生成的值都是连续的,不会产生空洞。

innodb_autoinc_lock_mode=1,这时候一条语句内不会产生空洞,但是语句之间可能会产生空洞。后面会有例子说明。

innodb_autoinc_lock_mode=2,如果有并发的insert操作,那么同一条语句内都可能产生空洞。

mixed-mode inserts

这种模式下针对innodb_autoinc_lock_mode的值配置不同,结果也会不同,当然innodb_autoinc_lock_mode=0时时不会产生空洞的,而innodb_autoinc_lock_mode=1以及innodb_autoinc_lock_mode=2是会产生空洞的。后面例子说明。

另外注意的一点是,在master-slave这种架构中,复制如果采用statement-based replication这种方式,则innodb_autoinc_lock_mode=0或1才是安全的。而如果是采用row-based replication或者mixed-based replication,则innodb_autoinc_lock_mode=0,1,2都是安全的。

4.实例

测试的两个表分别为t和t1,定义分别如下:

CREATE TABLE `t` (

`a` bigint(20) unsigned NOT NULL AUTO_INCREMENT,

PRIMARY KEY (`a`)

) ENGINE=InnoDB;

CREATE TABLE `t1` (

`c1` int(11) NOT NULL AUTO_INCREMENT,

`c2` varchar(10) DEFAULT NULL,

PRIMARY KEY (`c1`)

) ENGINE=InnoDB

首先在表t插入1-10000000共1千万条数据,为了后面测试方便。开启session1,执行下面语句:

insert into t1(c2) select * from t;

然后开启session2,在t1中插入数据:

insert into t1(c2) values(400);

针对innodb_autoinc_lock_mode不同的情况,新插入的数据的c1的值也不同。

innodb_autoinc_lock_mode=0时,因为session1的语句都是加AUTO-INC锁,因此,session1先开始的话,c1列的值都是1-10000000连续的值,由于在传统机制下,auto_increment值都是一个个分配,因此session2插入的数据c1的值则是10000001。最终看到的就是有两条这样的数据(400,400),(10000001,400)。

innodb_autoinc_lock_mode=1时,同样session1也会加AUTO-INC锁,但是由于该模式下会预先分配auto_increment的值,所以可以看到在session2中插入的数据的c1值不会是10000001,但是不会是1-10000000这其中的数字,因为session1有加AUTO-INC锁。最终的数据会是这样两条:(400,400), (10026856,400)。

innodb_autoinc_lock_mode=2时,session1不会加AUTO-INC锁,因此虽然session2是后执行,但是并不影响auto_increment值分配,最终的值跟我们执行session2的时间有关,最终的值可能是这样的:(400,400),(1235603,400)这样的,会占用1-10000000之间的值。

5.另外几点

1)关于innodb_autoinc_lock_mode=1时,auto_increment预先分配策略可以参照参考资料2,假定表t中已经初始有一条记录1,然后在表t中我们用````insert into t select NULL from t执行四次,可以看到表t中最终的记录会是1,2,3,4,6,7,8,9,13,14,15,16,17,18,19,20```,其中5,10,11,12都浪费掉了。参考资料1后面部分也有讲到预分配问题。

2)INSERT INTO t1...SELECT ... FROM t这类语句会对表t1加record lock,如果隔离级别是read committed,或者设置了innodb_locks_unsafe_for_binlog且隔离级别不是serialize,则不会对t加锁,否则对t加shared next-key lock。

6.参考资料

mysql 自增 空洞_MySQL auto_increment空洞问题相关推荐

  1. mysql自增字段_MySQL自增字段的常用语句

    学习MySQL数据库,MySQL自增字段是最基础的部分之一,下面为您介绍一些MySQL自增字段的常用语句,希望对您学习MySQL自增字段能些许帮助. 1.创建表格时添加: create table t ...

  2. MySQL自增主键auto_increment原理 与 自增主键出现间隙不连续现象的定位

    一.背景: 1.1.业务描述与SQL: 为了保存机器上报信息(业务需求是每个机器只需保存最新的一条记录),原 SQL 语句如下(其中,machineId 的为唯一索引,t_report_pad 的 i ...

  3. mysql自增类型_Mysql自增字段

    1.关键字 auto_increment 2.自增用法 例: CREATE TABLE animals ( id mediumint not null auto_increment, name cha ...

  4. mysql 自增语句_Mysql 自动增加设定基值的语句 | 很文博客

    有时候需要为自动增加的字段设置基值,用下面的语句即可. 核心代码: ALTER TABLE 表名 AUTO_INCREMENT = 设定的值 MySQL ALTER语法中ALTER [IGNORE] ...

  5. mysql自增默认值_mysql auto_increment自增初始值与步长

    MySQl自增列起始与步长 自增初始值 一.alter的使用 默认初始值为1 步长为1的情况 1.建表 2.插入数据 当插入数据可以看到步长为1,起始值为1 3.增加一列数据时 默认在加上步长 4.删 ...

  6. mysql自动增长id 溢出_MySQL表自增id溢出的故障复盘怎么解决 MySQL表自增id溢出的故障复盘解决方法...

    MySQL表自增id溢出的故障复盘如何解决?本篇文章小编给大家分享一下MySQL表自增id溢出的故障复盘解决方法,小编觉得挺不错的,现在分享给大家供大家参考,有需要的小伙伴们可以来看看. 问题:MyS ...

  7. mysql自增id用完了_MySQL表自增id用完了该怎么办?

    我们知道MySQL表可以定义一个自增长的id,如果我们的表没有指定主键字段,那MySQL会给我们的表创建一个不可见的,长度为6个自己的row_id,然后不停地往上加步长,虽然生活中自然数是没有上限的, ...

  8. mysql的增_MySQL之增_insert-replace

    MySQL增删改查之增insert.replace 一.INSERT语句 带有values子句的insert语句,用于数据的增加 语法: INSERT [INTO] tbl_name[(col_nam ...

  9. mysql自增id用完了_MySQL 自增 ID 用完了怎么办?

    MySQL 自增 ID 用完了怎么办? 在MySQL中有很多类型的自增ID,每个自增ID都设置了初始值,然后按照一定的步长增加,只要定义了字节长度,那么就会有上限,如果达到上限再次添加,则会报主键冲突 ...

  10. mysql设置id起点_mysql自增ID起始值修改方法

    在MysqL中很多朋友都认为字段为AUTO_INCREMENT类型自增ID值是无法修改,其实这样理解是错误的,下面介绍MysqL自增ID的起始值修改与设置方法. 通常的设置自增字段的方法:创建表格时添 ...

最新文章

  1. css3树形图,CSS3 树形堆栈
  2. linux 硬件抽象,Linux 内核硬件抽象
  3. 【Git笔记3】关于撤销、删除、恢复的那些事儿
  4. mybatis XML格式日期的处理 还有 日期对比的sql
  5. 2013_chengdu_visit
  6. java排列组合算法优缺点,一招彻底弄懂!
  7. mysql5.7.17 win7_win7下mysql5.7.17安装配置方法图文教程
  8. 要闻君说:谷歌云重磅发布两大技术平台;以后可以打飞滴了?SAP重组动荡;微软宣布 Azure Functions 支持 Java...
  9. Apache Shiro 使用手册(五)Shiro 配置说明
  10. 终于找到个好办法备份数据库了
  11. python基于django的高校教师科研成果管理系统
  12. STATA软件进行meta分析,在界面显示meta分析
  13. 我们项目中的VO、DTO、DO、PO有什么区别,你用对了吗?
  14. 从十七年蝉谈起——hashtable的表格大小为什么要选择质数
  15. 企鹅日记(十一):账号管理与ACL权限设置
  16. Redis集群cluster:增加删除(伸缩)节点操作步骤
  17. 【教程】通过lothelper实现短信接收测试
  18. 多米诺骨牌java_通过递归和回溯找到所有可能的多米诺骨牌链
  19. java容器都有哪些
  20. 帝国CMS 7.2 蓝色响应式网站模板自适应宽屏智能整站源码 A1

热门文章

  1. Niushop开源商城:618电商大趴开始预热啦!你的开源商城系统呢?
  2. kafka-生产者使用
  3. vsCode无法自动换行显示
  4. 【Java】根据生日计算年龄
  5. VB中数组的嵌套循环
  6. 推荐系统中的bais系统(二)—Select Bais和Exposure Bias
  7. Glide 的 transformation
  8. EMV学习过程中问题解决及汇总
  9. OpenGL VBO, PBO与FBO
  10. 什么是SDK? {转载}