mysql 自增值&自增锁
Posted 翔之天空
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql 自增值&自增锁相关的知识,希望对你有一定的参考价值。
参考:《mysql内核:innodb存储引擎》 第九章
mysql实战45讲—自增主键为什么不是连续的?
[MySQL源码] Innodb如何处理auto_inc值:https://yq.aliyun.com/articles/40926
[MySQL Bug]bug#61209简析:https://yq.aliyun.com/articles/40929
测试环境:Mysql 5.6.43版本
--2、上述5个问题的测试
--数据库参数设置
mysql> show variables like '%auto_increment%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| auto_increment_increment | 10 | //自增值递增的间隔,测试设置值为 10
| auto_increment_offset | 5 | //自增值的起始偏移,测试设置值为 5
+--------------------------+-------+
2 rows in set (0.00 sec)
mysql> show variables like '%innodb_autoinc_lock_mode%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_autoinc_lock_mode | 1 | //申请后立即释放,但批量操作 比如 insert...select 、load data 之类的操作 还是回退到0的方式 即提交结束后 释放自增锁
+--------------------------+-------+
1 row in set (0.00 sec)
--创建测试表并测试数据,id为自增值 ,以5位偏移量 、每次自增10
mysql> create table test_table_autoincr
(id int auto_increment,
name varchar(10),
id_card int,
primary key(id),
unique key idx_test_table_autoincr_id_card(id_card)
)engine=innodb;
Query OK, 0 rows affected (0.22 sec)
--插入测试数据
insert into test_table_autoincr values (null,'aa',11);
commit;
--如上问题1的现象:数据库重启后,会取得该表的最大自增值(SELECT MAX(col_name) FROM TABLE) +1 赋给下一条记录,可能会造成自增值的回退
--插入待删除的name=aa_del的一条数据,然后删除
insert into test_table_autoincr values (null,'aa_del',1111);
commit;
mysql> delete from test_table_autoincr where id=15;
mysql> commit;
mysql> select * from test_table_autoincr;
+----+------+---------+
| id | name | id_card |
+----+------+---------+
| 5 | aa | 11 |
+----+------+---------+
1 row in set (0.00 sec)
--查看下个自增值为AUTO_INCREMENT=25,因上个自增值为15的值被删除了
mysql> show create table test_table_autoincr\\G
*************************** 1. row ***************************
Table: test_table_autoincr
Create Table: CREATE TABLE `test_table_autoincr` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) COLLATE utf8_bin DEFAULT NULL,
`id_card` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_test_table_autoincr_id_card` (`id_card`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)
--重启数据库
[root@localmysql-source ~]# service mysql restart
Shutting down MySQL....[ OK ]
Starting MySQL.[ OK ]
--自增值回退为AUTO_INCREMENT=6 也就是该表的最大自增值(SELECT MAX(col_name) FROM TABLE) +1 (即是目前自增值的间隔是10的情况下 也是+1,下次插入自增值时 会重新计算 )
mysql> show create table test_table_autoincr\\G
*************************** 1. row ***************************
Table: test_table_autoincr
Create Table: CREATE TABLE `test_table_autoincr` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) COLLATE utf8_bin DEFAULT NULL,
`id_card` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_test_table_autoincr_id_card` (`id_card`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)
--继续插入数据
insert into test_table_autoincr values (null,'bb',22),(null,'cc',33),(null,'dd',44);
commit;
--查看当前表数据,发现自增值连续,不受到之前delete数据的影响
mysql> select * from test_table_autoincr;
+----+------+---------+
| id | name | id_card |
+----+------+---------+
| 5 | aa | 11 |
| 15 | bb | 22 |
| 25 | cc | 33 |
| 35 | dd | 44 |
+----+------+---------+
4 rows in set (0.00 sec)
--查看表状态,可看下个自增值为AUTO_INCREMENT=45
mysql> show create table test_table_autoincr\\G
*************************** 1. row ***************************
Table: test_table_autoincr
Create Table: CREATE TABLE `test_table_autoincr` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) COLLATE utf8_bin DEFAULT NULL,
`id_card` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_test_table_autoincr_id_card` (`id_card`)
) ENGINE=InnoDB AUTO_INCREMENT=45 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)
--如上问题2的现象:id_card唯一索引冲突后,下一条自增值不连续了。 45的自增值因唯一索引冲突而丢失
mysql> insert into test_table_autoincr values (null,'ee',44);
ERROR 1062 (23000): Duplicate entry '44' for key 'idx_test_table_autoincr_id_card'
mysql> insert into test_table_autoincr values (null,'ee',55);
mysql> select * from test_table_autoincr;
+----+------+---------+
| id | name | id_card |
+----+------+---------+
| 5 | aa | 11 |
| 15 | bb | 22 |
| 25 | cc | 33 |
| 35 | dd | 44 |
| 55 | ee | 55 |
+----+------+---------+
5 rows in set (0.00 sec)
--如上问题3的现象:事务回滚后,下一条自增值不连续了。65的自增值因为回滚而丢失
mysql> begin;
mysql> insert into test_table_autoincr values (null,'ff',66);
mysql> rollback;
mysql> insert into test_table_autoincr values (null,'ff',66);
mysql> commit;
mysql> select * from test_table_autoincr;
+----+------+---------+
| id | name | id_card |
+----+------+---------+
| 5 | aa | 11 |
| 15 | bb | 22 |
| 25 | cc | 33 |
| 35 | dd | 44 |
| 55 | ee | 55 |
| 75 | ff | 66 |
+----+------+---------+
6 rows in set (0.00 sec)
--如上问题4的现象:插入给定的自增值505,会造成和上一条的75自增值不连续了。
mysql> insert into test_table_autoincr values (505,'gg',77);
mysql> insert into test_table_autoincr values (null,'hh',88);
mysql> commit;
mysql> select * from test_table_autoincr;
+-----+------+---------+
| id | name | id_card |
+-----+------+---------+
| 5 | aa | 11 |
| 15 | bb | 22 |
| 25 | cc | 33 |
| 35 | dd | 44 |
| 55 | ee | 55 |
| 75 | ff | 66 |
| 505 | gg | 77 |
| 515 | hh | 88 |
+-----+------+---------+
8 rows in set (0.00 sec)
--其中如果不是按照自增值间隔和偏移量的规律来的话, 会造成和上一条以及下一条的自增值同时不连续了。 插入1000后 不符合自增长间隔和偏移量,下一条自增值要符合规律变为1005 造成不连续
mysql> insert into test_table_autoincr values (1000,'ii',99);
mysql> insert into test_table_autoincr values (null,'jj',110);
mysql> insert into test_table_autoincr values (null,'kk',220);
mysql> commit;
mysql> select * from test_table_autoincr;
+------+------+---------+
| id | name | id_card |
+------+------+---------+
| 5 | aa | 11 |
| 15 | bb | 22 |
| 25 | cc | 33 |
| 35 | dd | 44 |
| 55 | ee | 55 |
| 75 | ff | 66 |
| 505 | gg | 77 |
| 515 | hh | 88 |
| 1000 | ii | 99 |
| 1005 | jj | 110 |
| 1015 | kk | 220 |
+------+------+---------+
11 rows in set (0.00 sec)
--如上问题5的现象:批量插入时,会分配的自增值个数为上一次的2倍, 这样分配下来 会造成一些自增值浪费 造成下一条自增值不连续了。
--测试表test_table_autoincr_2 插入4条数据,分四次申请的,第一次申请1个,第二次申请2个,第三次申请4个,这样就是申请了7个 但实际只有4条数据,
create table test_table_autoincr_2 like test_table_autoincr;
insert into test_table_autoincr_2 values (null,'qwer',1111);
insert into test_table_autoincr_2 values (null,'adsf',2222);
insert into test_table_autoincr_2 values (null,'zxcv',3333);
insert into test_table_autoincr_2 values (null,'uiop',4444);
commit;
mysql> select * from test_table_autoincr_2;
+----+------+---------+
| id | name | id_card |
+----+------+---------+
| 5 | qwer | 1111 |
| 15 | adsf | 2222 |
| 25 | zxcv | 3333 |
| 35 | uiop | 4444 |
+----+------+---------+
4 rows in set (0.00 sec)
mysql> show create table test_table_autoincr_2\\G
*************************** 1. row ***************************
Table: test_table_autoincr_2
Create Table: CREATE TABLE `test_table_autoincr_2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) COLLATE utf8_bin DEFAULT NULL,
`id_card` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_test_table_autoincr_id_card` (`id_card`)
) ENGINE=InnoDB AUTO_INCREMENT=45 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)
--测试表test_table_autoincr_3复制test_table_autoincr_2的数据后,再插入一条数据后,45、55、65自增值丢失 不连续了, 从第8个开始
create table test_table_autoincr_3 like test_table_autoincr_2;
insert into test_table_autoincr_3(name,id_card) select name,id_card from test_table_autoincr_2;
mysql> select * from test_table_autoincr_3;
+----+------+---------+
| id | name | id_card |
+----+------+---------+
| 5 | qwer | 1111 |
| 15 | adsf | 2222 |
| 25 | zxcv | 3333 |
| 35 | uiop | 4444 |
+----+------+---------+
4 rows in set (0.00 sec)
mysql> show create table test_table_autoincr_3\\G
*************************** 1. row ***************************
Table: test_table_autoincr_3
Create Table: CREATE TABLE `test_table_autoincr_3` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) COLLATE utf8_bin DEFAULT NULL,
`id_card` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_test_table_autoincr_id_card` (`id_card`)
) ENGINE=InnoDB AUTO_INCREMENT=75 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)
mysql> insert into test_table_autoincr_3 values (null,'hjlk',5555);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test_table_autoincr_3;
+----+------+---------+
| id | name | id_card |
+----+------+---------+
| 5 | qwer | 1111 |
| 15 | adsf | 2222 |
| 25 | zxcv | 3333 |
| 35 | uiop | 4444 |
| 75 | hjlk | 5555 |
+----+------+---------+
5 rows in set (0.00 sec)
以上是关于mysql 自增值&自增锁的主要内容,如果未能解决你的问题,请参考以下文章