之前有碰到过开发同事指出一张InnoDB表的自增列 AUTO_INCREMENT 值莫明的变大,由于这张表是通过mysqldump导出导入的。

问题排查:

1、首先,查看表表义的sql部分的 auto_increment 值部分是正常,所以排除是导入表问题所引起的;

2、最后,经过沟通了解怀疑是插入时指定自增列的值,并且值过大,随之发现自增列的值出错时又进行大量删除时引起的问题。

为了验证这个怀疑的准确性,同时学习下InnoDB处理 AUTO_INCREMENT 的机制,因此在测试环境做了测试总结。

本文使用的MySQL版本为官方社区版 5.7.24

(root@localhost) [test] > select version();
+------------+
| version() |
+------------+
| 5.7.24-log |
+------------+
1 row in set (0.00 sec)

测试环境测试表参考官方文档:https://dev.mysql.com/doc/refman/5.7/en/innodb-auto-increment-handling.html

(root@localhost) [test] > show create table t1\G;
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`c1` int(11) NOT NULL AUTO_INCREMENT,
`c2` varchar(10) DEFAULT NULL,
PRIMARY KEY (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec) (root@localhost) [test] > show columns from t1;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| c1 | int(11) | NO | PRI | NULL | auto_increment |
| c2 | varchar(10) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

AUTO_INCREMENT锁模式

InnoDB自增锁的模式由参数 innodb_autoinc_lock_mode 在启动时指定,这是一个只读参数,并不能在实例运行中动态修改参数值。参数值选项如下:

innodb_autoinc_lock_mode = {0|1|2}

InnoDB表insert语句主要可以分为三种类型:

  • Simple inserts(简单插入)

    可以通过语句预先判断插入的行数。包括不包含子查询的单行、多行 INSERTREPLACE 语句,还有语句 INSERT ... ON DUPLICATE KEY UPDATE

  • Bulk inserts(大量插入)

    无法通过语句预先判断插入的行数。包括 INSERT ... SELECTREPLACE ... SELECTLOAD DATA 语句。InnoDB每处理一行才会为 AUTO_INCREMENT 列分配一个值。

  • Mixed-mode inserts(混合模式插入)

    在简单插入语句当中,有的行有为自增列指定值,而有的行没有为自增列指定值。例如:

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

其中c1为自增列。

还有一种混合模式插入语句 INSERT ... ON DUPLICATE KEY UPDATE ,这类语句InnoDB为自增列预分配的值有可能被使用,也有可能不被使用。

传统锁模式 (traditional)

innodb_autoinc_lock_mode = 0

在这个模式下,所有的 INSERT 语句在插入有自增属性的列时都要获取一个特殊的 AUTO-INC 表级锁。该锁的持有时间到语句结束(而不是到事务结束,一个事务中可能包含多条语句),它能够确保为有自增属性列在 INSERT 一行或者多行数据时分配连续递增的值。

连续锁模式 (consecutive)

innodb_autoinc_lock_mode = 1

这是默认的锁模式。在这个模式下,大量插入每条语句执行时都将获得特殊的表级 AUTO-INC 锁,语句执行完成后释放。每次只有一条语句可以执行并持有 AUTO-INC 锁。

  • Bulk inserts(大量插入)

    • 如果大量插入的源表和目标表是不同的,则在源表第一行数据获得共享锁之后,目标表就加上 AUTO-INC 表锁;
    • 如果大量插入的源表和目标表是同一张表,当源表选取所有行获得共享锁之后,目标表才加上 AUTO-INC 表锁。
  • Simple inserts(简单插入)
    • 通过 MUTEX(轻量级的锁) 而不是 AUTO-INC特殊表锁控制插入分配自增属性列的值;
    • MUTEX 只在分配值的过程中持有,而无需等到语句结束,并且性能花销更少;
    • 简单插入不持有 AUTO-INC 锁,但如果其他事务持有,需等待其他事务释放,就像大量插入那样。

交叉锁模式 (interleaved)

innodb_autoinc_lock_mode = 2

在这种锁模式下,没有插入语句使用 AUTO-INC 表级锁,并且多条语句可以并发执行。这是最快并且扩展性最好的锁模式,但是如果binlog使用基于语句级复制的在从库重放SQL语句时是不安全的。

AUTO_INCREMENT锁模式使用说明

用于复制

  • 基于语句级别的复制,当 innodb_autoinc_lock_mode = 0 | 1 时,主从使用的是相同的自增列值。当 innodb_autoinc_lock_mode = 2 或者主从使用不同的 innodb_autoinc_lock_mode 时,主从无法保证使用相同的自增列值;
  • 基于行级别和复合模式的复制,innodb_autoinc_lock_mode 的所有取值都是安全的,因为SQL语句执行顺序对基于行级别的复制没影响。

自增值缺失与间隙

无论 AUTO_INCREMENT 处于哪种锁模式下,即 innodb_autoinc_lock_mode 的所有取值情况下,在一个事务当中自增列分配的值是不能被回滚的。这会导致事务回滚了,但是自增列分配的值却消失了,自增列分配的值是无法随着事务回滚而重复利用,这样就自增列上的值就产生了间隙。

测试:

--1、 开启一个事务
(root@localhost) [test] > begin;
Query OK, 0 rows affected (0.00 sec) (root@localhost) [test] > insert into t1(c2) values('aa');
Query OK, 1 row affected (0.00 sec) (root@localhost) [test] > select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 1 | aa |
+----+------+
1 row in set (0.00 sec) (root@localhost) [test] > show create table t1\G;
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`c1` int(11) NOT NULL AUTO_INCREMENT,
`c2` varchar(10) DEFAULT NULL,
PRIMARY KEY (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
1 row in set (0.00 sec) --2、事务回滚,不提交
(root@localhost) [test] > rollback;
Query OK, 0 rows affected (0.00 sec) (root@localhost) [test] > select * from t1;
Empty set (0.00 sec) (root@localhost) [test] > show create table t1\G;
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`c1` int(11) NOT NULL AUTO_INCREMENT,
`c2` varchar(10) DEFAULT NULL,
PRIMARY KEY (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
1 row in set (0.00 sec) --3、再次开启事务,插入值并提交
(root@localhost) [test] > begin;
Query OK, 0 rows affected (0.00 sec) (root@localhost) [test] > insert into t1(c2) values('aaa');
Query OK, 1 row affected (0.02 sec) (root@localhost) [test] > select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 2 | aaa |
+----+------+
1 row in set (0.00 sec) (root@localhost) [test] > commit;
Query OK, 0 rows affected (0.00 sec) (root@localhost) [test] > show create table t1\G;
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`c1` int(11) NOT NULL AUTO_INCREMENT,
`c2` varchar(10) DEFAULT NULL,
PRIMARY KEY (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

可以看出自增列分配的值是不会再出现 c1=1 的。

为自增列指定NULL或者0

无论 AUTO_INCREMENT 处于哪种锁模式下,如果在 INSERT 语句为自增列指定 NULL 或者 0 时,InnoDB认为并没有为自增列指定值,同时也会为其分配值。

测试:

(root@localhost) [test] > select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 2 | aaa |
+----+------+
1 row in set (0.00 sec) (root@localhost) [test] > insert into t1 values(NULL,'bbb'),(0,'ccc');
Query OK, 2 rows affected (0.02 sec)
Records: 2 Duplicates: 0 Warnings: 0 (root@localhost) [test] > select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 2 | aaa |
| 3 | bbb |
| 4 | ccc |
+----+------+
3 rows in set (0.00 sec) (root@localhost) [test] > show create table t1\G;
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`c1` int(11) NOT NULL AUTO_INCREMENT,
`c2` varchar(10) DEFAULT NULL,
PRIMARY KEY (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

为自增列指定负值

无论 AUTO_INCREMENT 处于哪种锁模式下,自增列分配值机制不会生效,即为自增列指定负值是不影响 AUTO_INCREMENT 值的。

测试:

(root@localhost) [test] > SELECT auto_increment FROM information_schema.tables where table_schema='test' and table_name='t1';
+----------------+
| auto_increment |
+----------------+
| 5 |
+----------------+
1 row in set (0.00 sec) (root@localhost) [test] > insert into t1 values(-1,'ddd');
Query OK, 1 row affected (0.01 sec) (root@localhost) [test] > select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| -1 | ddd |
| 2 | aaa |
| 3 | bbb |
| 4 | ccc |
+----+------+
4 rows in set (0.00 sec) (root@localhost) [test] > SELECT auto_increment FROM information_schema.tables where table_schema='test' and table_name='t1';
+----------------+
| auto_increment |
+----------------+
| 5 |
+----------------+
1 row in set (0.00 sec)

自增列值大于列类型最大的整型值

无论 AUTO_INCREMENT 处于哪种锁模式下,自增列分配的值如果大于自增列所属字段类型的最大值,则自增列分配值机制就不会生效。

测试:

在MySQL当中,INT类型的最大值为 -2147483648~2147483647

(root@localhost) [test] > select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| -1 | ddd |
| 2 | aaa |
| 3 | bbb |
| 4 | ccc |
+----+------+
4 rows in set (0.00 sec) (root@localhost) [test] > SELECT auto_increment FROM information_schema.tables where table_schema='test' and table_name='t1';
+----------------+
| auto_increment |
+----------------+
| 5 |
+----------------+
1 row in set (0.00 sec) (root@localhost) [test] > insert into t1 values(2147483647,'eee');
Query OK, 1 row affected (0.02 sec) (root@localhost) [test] > select * from t1;
+------------+------+
| c1 | c2 |
+------------+------+
| -1 | ddd |
| 2 | aaa |
| 3 | bbb |
| 4 | ccc |
| 2147483647 | eee |
+------------+------+
5 rows in set (0.00 sec) (root@localhost) [test] > SELECT auto_increment FROM information_schema.tables where table_schema='test' and table_name='t1';
+----------------+
| auto_increment |
+----------------+
| 2147483647 |
+----------------+
1 row in set (0.00 sec)

可以看出自增列 AUTO_INCREMENT 并不会分配。

大量插入导致的间隙

  • 当参数值 innodb_autoinc_lock_mode 设置为 01 时,每条语句生成的自增列值都是连续的,不会产生间隙。因为表级 AUTO-INC 锁会一直持有直到语句结束,并且同一时间只有一条语句在执行;
  • 当参数值 innodb_autoinc_lock_mode 设置为 2 时,在大量插入时有可能会产生间隙,但是只有当并发执行 INSERT 语句时。
  • 对于设置成 1 或者 2 情形下,在连续的语句之间可能产生间隙,因为对于大量插入InnoDB并不清楚每条语句所需自增量值数量。

混合模式插入自增列值分配

测试表:

-- t1表:表中无数据,但自增列下一个分配值从101开始
(root@localhost) [test] > show create table t1\G;
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`c1` int(11) NOT NULL AUTO_INCREMENT,
`c2` varchar(10) DEFAULT NULL,
PRIMARY KEY (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8
1 row in set (0.00 sec) (root@localhost) [test] > select count(*) from t1;
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec) -- t2表:表中有100万行数据,并且自增列值是连续的
(root@localhost) [test] > show create table t2\G;
*************************** 1. row ***************************
Table: t2
Create Table: CREATE TABLE `t2` (
`c1` int(11) NOT NULL AUTO_INCREMENT,
`c2` varchar(10) DEFAULT NULL,
PRIMARY KEY (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8
1 row in set (0.00 sec) (root@localhost) [test] > select count(*) from t2;
+----------+
| count(*) |
+----------+
| 1000000 |
+----------+
1 row in set (0.22 sec) (root@localhost) [test] > select min(c1),max(c1) from t2;
+---------+---------+
| min(c1) | max(c1) |
+---------+---------+
| 1 | 1000000 |
+---------+---------+
1 row in set (0.01 sec)

innodb_autoinc_lock_mode = 0

(root@localhost) [test] > show global variables like 'innodb_autoinc_lock_mode';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_autoinc_lock_mode | 0 |
+--------------------------+-------+
1 row in set (0.02 sec) -- 1、开启session 1执行TRX1
TRX1: insert into t1(c2) select c2 from t2; (root@localhost) [test] > insert into t1(c2) select c2 from t2;
Query OK, 1000000 rows affected (6.37 sec)
Records: 1000000 Duplicates: 0 Warnings: 0 -- 2、在TRX1执行期间开启session 2执行TRX2
TRX2: insert into t1 (c1,c2) values (1,'test_inc_a'), (NULL,'test_inc_b'), (5,'test_inc_c'), (0,'test_inc_d'); (root@localhost) [test] > insert into t1 (c1,c2) values (1,'test_inc_a'), (NULL,'test_inc_b'), (5,'test_inc_c'), (0,'test_inc_d');
Query OK, 4 rows affected (5.01 sec)
Records: 4 Duplicates: 0 Warnings: 0 -- 3、查看TRX2插入值的记录
(root@localhost) [test] > select * from t1 where c2 in ('test_inc_a','test_inc_b','test_inc_c','test_inc_d');
+---------+------------+
| c1 | c2 |
+---------+------------+
| 1 | test_inc_a |
| 5 | test_inc_c |
| 1000101 | test_inc_b |
| 1000102 | test_inc_d |
+---------+------------+
4 rows in set (0.34 sec) -- 4、查看当前AUTO_INCREMENT值 (root@localhost) [test] > select auto_increment from information_schema.tables where table_schema='test' and table_name='t1';
+----------------+
| auto_increment |
+----------------+
| 1000103 |
+----------------+
1 row in set (0.00 sec)
  • TRX1:持有了 AUTO-INC 表锁,自动分配了101~1000100的自增值,并保证是连续的;
  • TRX2:需等待 AUTO-INC 锁的释放,自动分配了1000101~1000102的自增值。

可以看出下一个自增列值为 103,因为自增列的值是在每条插入语句执行时分配的,而不是一开始就分配完的。

innodb_autoinc_lock_mode = 1

(root@localhost) [test] > show global variables like 'innodb_autoinc_lock_mode';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_autoinc_lock_mode | 1 |
+--------------------------+-------+
1 row in set (0.01 sec) -- 1、开启session 1执行TRX1
TRX1: insert into t1(c2) select c2 from t2; (root@localhost) [test] > insert into t1(c2) select c2 from t2;
Query OK, 1000000 rows affected (5.88 sec)
Records: 1000000 Duplicates: 0 Warnings: 0 -- 2、在TRX1执行期间开启session 2执行TRX2
TRX2: insert into t1 (c1,c2) values (1,'test_inc_a'), (NULL,'test_inc_b'), (5,'test_inc_c'), (0,'test_inc_d'); (root@localhost) [test] > insert into t1 (c1,c2) values (1,'test_inc_a'), (NULL,'test_inc_b'), (5,'test_inc_c'), (0,'test_inc_d');
Query OK, 4 rows affected (4.38 sec)
Records: 4 Duplicates: 0 Warnings: 0 -- 3、查看TRX2插入值的记录
(root@localhost) [test] > select * from t1 where c2 in ('test_inc_a','test_inc_b','test_inc_c','test_inc_d');
+---------+------------+
| c1 | c2 |
+---------+------------+
| 1 | test_inc_a |
| 5 | test_inc_c |
| 1048661 | test_inc_b |
| 1048662 | test_inc_d |
+---------+------------+
4 rows in set (0.32 sec) -- 4、查看当前AUTO_INCREMENT值
(root@localhost) [test] > select auto_increment from information_schema.tables where table_schema='test' and table_name='t1';
+----------------+
| auto_increment |
+----------------+
| 1048665 |
+----------------+
1 row in set (0.00 sec) -- 5、查看t1表的前10行记录、后10行记录和总行数
(root@localhost) [test] > select * from t1 order by c1 asc limit 10;
+-----+------------+
| c1 | c2 |
+-----+------------+
| 1 | test_inc_a |
| 5 | test_inc_c |
| 101 | CwAkHbEWs |
| 102 | re |
| 103 | uqrQbj |
| 104 | SQ |
| 105 | ojyPkMA |
| 106 | 03qNqZ |
| 107 | G8J |
| 108 | Uo3 |
+-----+------------+
10 rows in set (0.00 sec) (root@localhost) [test] > select * from t1 order by c1 asc limit 999994,10;
+---------+------------+
| c1 | c2 |
+---------+------------+
| 1000093 | o4AzuR |
| 1000094 | NJMJJk |
| 1000095 | 0o5xPuDnE |
| 1000096 | QLLtImQC |
| 1000097 | N1Fojm |
| 1000098 | 6aZemarPC |
| 1000099 | 4OUGSM1JzL |
| 1000100 | l8g6J |
| 1048661 | test_inc_b |
| 1048662 | test_inc_d |
+---------+------------+
10 rows in set (0.32 sec) (root@localhost) [test] > select count(*) from t1;
+----------+
| count(*) |
+----------+
| 1000004 |
+----------+
1 row in set (0.17 sec)

在此模式下:

  • TRX1:大量插入时持有 AUTO-INC 表锁,自增列的值是预先分配的,101~10000100,总共100万个连续值;
  • TRX2:混合插入情况下,语句为简单 INSERT 语句,有的行自增列有指定值,而有的行没有,这时TRX2是无须等待持有 AUTO-INC 锁的,由于TRX1语句还在执行,InnoDB并不知道需要分配多少个自增列值,也不清楚TRX用了多少个自增列值,所以在并行执行 INSERT 时对于未指定行的自增列值分配就会产生间隙(1000100~1048661之间的间隙),但是语句当中分配的自增列(1048661和1048662)值依然是连续的。

可以看出下一个自增列值为 1048665 ,因为自增列值个数在语句执行开始就已经分配了4个(1048661~1048664),但实际语句只使用了2个。

innodb_autoinc_lock_mode = 2

(root@localhost) [test] > show global variables like 'innodb_autoinc_lock_mode';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_autoinc_lock_mode | 2 |
+--------------------------+-------+
1 row in set (0.01 sec) -- 1、开启session 1执行TRX1
TRX1: insert into t1(c2) select c2 from t2; (root@localhost) [test] > insert into t1(c2) select c2 from t2;
Query OK, 1000000 rows affected (4.67 sec)
Records: 1000000 Duplicates: 0 Warnings: 0 -- 2、在TRX1执行期间开启session 2执行TRX2
TRX2: insert into t1 (c1,c2) values (1,'test_inc_a'), (NULL,'test_inc_b'), (5,'test_inc_c'), (0,'test_inc_d'); (root@localhost) [test] > insert into t1 (c1,c2) values (1,'test_inc_a'), (NULL,'test_inc_b'), (5,'test_inc_c'), (0,'test_inc_d');
Query OK, 4 rows affected (0.02 sec)
Records: 4 Duplicates: 0 Warnings: 0 -- 3、查看TRX2插入值的记录
(root@localhost) [test] > select * from t1 where c2 in ('test_inc_a','test_inc_b','test_inc_c','test_inc_d');
+--------+------------+
| c1 | c2 |
+--------+------------+
| 1 | test_inc_a |
| 5 | test_inc_c |
| 262241 | test_inc_b |
| 262242 | test_inc_d |
+--------+------------+
4 rows in set (0.28 sec) -- 4、查看当前AUTO_INCREMENT值
(root@localhost) [test] > select auto_increment from information_schema.tables where table_schema='test' and table_name='t1';
+----------------+
| auto_increment |
+----------------+
| 1048665 |
+----------------+
1 row in set (0.00 sec) -- 5、查看t1表的前10行记录、后10行记录和总行数
(root@localhost) [test] > select * from t1 order by c1 asc limit 10;
+-----+------------+
| c1 | c2 |
+-----+------------+
| 1 | test_inc_a |
| 5 | test_inc_c |
| 101 | CwAkHbEWs |
| 102 | re |
| 103 | uqrQbj |
| 104 | SQ |
| 105 | ojyPkMA |
| 106 | 03qNqZ |
| 107 | G8J |
| 108 | Uo3 |
+-----+------------+
10 rows in set (0.00 sec) (root@localhost) [test] > select * from t1 order by c1 asc limit 999994,10;
+---------+------------+
| c1 | c2 |
+---------+------------+
| 1000095 | KHukB |
| 1000096 | bnpyaevl79 |
| 1000097 | o4AzuR |
| 1000098 | NJMJJk |
| 1000099 | 0o5xPuDnE |
| 1000100 | QLLtImQC |
| 1000101 | N1Fojm |
| 1000102 | 6aZemarPC |
| 1000103 | 4OUGSM1JzL |
| 1000104 | l8g6J |
+---------+------------+
10 rows in set (0.22 sec) (root@localhost) [test] > select count(*) from t1;
+----------+
| count(*) |
+----------+
| 1000004 |
+----------+
1 row in set (0.17 sec) -- 6、查看TRX2插入时产生间隙位置行记录
(root@localhost) [test] > select * from t1 where c1 between 262240 and 262250;
+--------+------------+
| c1 | c2 |
+--------+------------+
| 262240 | mNfx37 |
| 262241 | test_inc_b |
| 262242 | test_inc_d |
| 262245 | Taqi |
| 262246 | Ojpakez |
| 262247 | 2fKtmm6rW |
| 262248 | AysE |
| 262249 | wqWGs |
| 262250 | lC |
+--------+------------+
9 rows in set (0.00 sec)

在此模式下:

  • TRX1:因为大量插入时是不持有 AUTO-INC 表锁,所在TRX1在执行期间,TRX2很快就插入完成并分配了自增列值。
  • TRX2:简单插入语句时可以判断出须分配4个自增列值,但是只使用了2个,这样就造成了2个间隙(262243~262244)。

可以看出下一个自增列值为 1048665 ,TRX1执行时因为大量插入时无法预估插入值数量,TRX2执行时分配了4个自增值,但只使用了2个(262241~262242),造成了2个间隙,TRX1和TRX2的自增列值是交叉分配的。

修改自增列当中的值

无论 AUTO_INCREMENT 处于哪种锁模式下,更新自增列的值都有可能会产生 Duplicate entry 重复值错误。

-- 测试表:

(root@localhost) [test] > create table t3 (c1 int not null auto_increment primary key);
Query OK, 0 rows affected (0.06 sec) (root@localhost) [test] > show create table t3\G;
*************************** 1. row ***************************
Table: t3
Create Table: CREATE TABLE `t3` (
`c1` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec) -- 1、插入几行数据
(root@localhost) [test] > insert into t3 values(0),(0),(3);
Query OK, 3 rows affected (0.04 sec)
Records: 3 Duplicates: 0 Warnings: 0 (root@localhost) [test] > select * from t3;
+----+
| c1 |
+----+
| 1 |
| 2 |
| 3 |
+----+
3 rows in set (0.00 sec) (root@localhost) [test] > select auto_increment from information_schema.tables where table_schema='test' and table_name='t3';
+----------------+
| auto_increment |
+----------------+
| 4 |
+----------------+
1 row in set (0.00 sec) -- 2、更新c1值为5
(root@localhost) [test] > update t3 set c1 = 5 where c1 = 3;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0 (root@localhost) [test] > select * from t3;
+----+
| c1 |
+----+
| 1 |
| 2 |
| 5 |
+----+
3 rows in set (0.00 sec) (root@localhost) [test] > select auto_increment from information_schema.tables where table_schema='test' and table_name='t3';
+----------------+
| auto_increment |
+----------------+
| 4 |
+----------------+
1 row in set (0.00 sec) -- 3、再次插入几行数据
(root@localhost) [test] > insert into t3 values(0),(0),(3);
ERROR 1062 (23000): Duplicate entry '5' for key 'PRIMARY' (root@localhost) [test] > select * from t3;
+----+
| c1 |
+----+
| 1 |
| 2 |
| 5 |
+----+
3 rows in set (0.00 sec)

可以看出更新了自增列的值之后产生了 Duplicate entry 重复值错误。建议不要更新自增列的值,或是把自增列值更新为大于 AUTO_INCREMENT 的值。

AUTO_INCREMENT计数器的初始化

当为一张InnoDB表指定自增列时,此时表在数据字典中维护着一个特殊的计数器为自增列分配值,名称为 auto_increment 计数器,这个计数器是存储中内存中,而不是在磁盘上。

当服务器重启之后后,为了初始化 auto_increment 计数器,InnoDB执行如下等效语句来确定自增列下个需要分配的值:

SELECT MAX(ai_col) FROM table_name FOR UPDATE;

默认的,自增列的下一个分配值由以上语句得出的值增加 1 ,这个增加步长由参数 auto_increment_increment 决定。如果是空表,则自增列的下一个分配值为 1 ,这个空表时默认值由参数 auto_increment_offset 决定。

  • 当表中指定自增列,如果没有为自增列值指定值,则由计数器分配。如果为自增列指定了值,还是插入指定的值。如果指定插入值大于计数器的值,同时计数器也会跟着调整,这样容易造成间隙。
  • 由于计数据器是运行在内存中,所以当服务器重启后,InnoDB会重新初始化计数器。
  • 服务器重启的同时也重置了之前 CREATE TABLEALTER TABLE 为表指定的 AUTO_INCREMENT 值,可以再次通过语句进行指定。

参考

https://dev.mysql.com/doc/refman/5.7/en/innodb-auto-increment-handling.html

☆〖本人水平有限,文中如有错误还请留言批评指正!〗☆

MySQL AUTO_INCREMENT 学习总结的更多相关文章

  1. MySQL数据库学习笔记(十二)----开源工具DbUtils的使用(数据库的增删改查)

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  2. Mysql事件学习

    出自:http://blog.chinaunix.net/uid-20639775-id-3323098.html Mysql事件学习 在系统管理或者数据库管理中,经常要周期性的执行某一个命令或者SQ ...

  3. MySQL基础学习笔记

    一.数据库基本概念 1.1 数据库 - Database 从字面意思看,数据库就是一个存储数据的仓库.从计算机的角度来讲,数据库(Datebase)是按照数据结构来组织.存储和管理数据的仓库. 简单来 ...

  4. MySQL命令学习

    上面两篇博客讲了MySQL的安装.登录,密码重置,为接下来的MySQL命令学习做好了准备,现在开启MySQL命令学习之旅吧. 首先打开CMD,输入命令:mysql -u root -p  登录MySQ ...

  5. mysql应用学习-在cmd命令窗口下创建数据库和表

    运行以下操作,请确认您已经正确安装和配置了mysql. 首先要运行cmd.exe,进入命令窗口. step1. 进入MySQL monitor 如果您已登录mysql,可直接进入step2;若未登录请 ...

  6. mysql深入学习(一)

    Mysql高级学习 一.Mysql简介 1.概述 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司. MySQL是一种关联数据库管理系统,将数据保存在不同 ...

  7. 基于【 MySql 】二 || mysql详细学习笔记

    mysql重点学习笔记 /* Windows服务 */ -- 启动MySQL net start mysql -- 创建Windows服务 sc create mysql binPath= mysql ...

  8. 进入mysql的学习>从零开始学JAVA系列

    目录 MySQL的学习 什么是MYSQL 安装MYSQL Window安装MYSQL(压缩包版) 什么是MYSQL 安装MYSQL Window安装MYSQL(压缩包版) MYSQL基本指令 DDL ...

  9. Mysql基础学习第二天

    Mysql基础学习第二天 函数 函数:是指一段可以直接被另一段程序调用的程序或代码. 字符串函数 数值函数 日期函数 流程函数 字符串函数 MySQL内置很多字符串函数,常用的几个如下: 函数 功能 ...

随机推荐

  1. nginx实现限速

    项目中有一个需求,需要限制每个容器的网速,避免某些容器占用太多资源,导致其他容器无法使用,但是docker对于网速的限制支持的有点弱,由于容器中的所有进程和APP的交互都是通过nginx的,所以就想到 ...

  2. phpunit assert断言分类整理

    布尔类型 方法名 含义 参数 返回值 assertTrue 断言为真     assertFalse 断言为假     NULL类型 方法名 含义 参数 返回值 assertNull 断言为NULL ...

  3. 在Java路上,我看过的一些书、源码和框架(转)

    原文地址:http://www.jianshu.com/p/4a41ee88bd82 物有本末,事有终始,知所先后,则近道矣 面试经历 关于Java面试,你应该准备这些知识点关于Java面试,你应该准 ...

  4. minicom for Mac 配置

    安装:brew install minicom 配置: 串口是:dev/tty.usbserial minicons -s 配置一下: 流控要关掉,不然输入不了

  5. C++系统自己主动生成默认构造函数的情况

    (1) 基类存在默认构造函数 class CBaseClass { public: CBaseClass() { m_i = 0; } private: int m_i; }; class CDriv ...

  6. jquery Ajax请求示例,jquery Ajax基本请求方法示例

    jquery Ajax请求示例,jquery Ajax基本请求方法示例 ================================ ©Copyright 蕃薯耀 2018年5月7日 https: ...

  7. N76E003的环境搭建

    一.准备工作: 1.下载编译工具keil c51 2.下载N76E003提供的板级支持包(BSP),可到nuvoton上下载   二.开发环境搭建 1.安装keil c51,然后和谐...不能随便发链 ...

  8. go语言学习笔记1 Go开发环境

    什么是Go?Go是一门并发支持.垃圾回收的编译型系统编程语言,旨在创造一门具有在静态编译语言的高性能和动态语言的高效开发之间拥有良好平衡的一门编程语言. Go的主要特点有哪些?* 类型安全 和 内存安 ...

  9. Dart 语言简易教程系列

    google Fuchsia系统 及 dart语言简介 在 InteIIiJ IDEA 中搭建 Dart 的开发环境 Dart Linux 开发环境搭建 Dart 语言简易教程(一) Dart 语言简 ...

  10. SQLite可视化工具SQLite studio

    特点: 1.轻量级2.独立性,没有依赖,无需安装3.隔离性 全部在一个文件夹系统4.跨平台 支持众多操作系统5.多语言接口 支持众多编程语言6.安全性 事物,通过独占性和共享锁来实现独立事务的处理,多 ...