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 自增值&自增锁的主要内容,如果未能解决你的问题,请参考以下文章

MySQL AutoIncrement--自增锁模式

MySQL--自增列持久化问题

MySQL的自增id用尽时的解决方案

MySQL - 全局锁表级锁行级锁元数据锁自增锁意向锁共享锁独占锁记录锁间隙锁临键锁死锁

自增锁引发的悲剧

Oracle 怎样查询所有自增的序列名