删除行对MySQL序列有这么多影响?
墨墨导读:MySQL序列概述为了达到标识的目的,许多应用程序需要生成唯一编号,比如:商品编号、交易流水号等。
一、MySQL序列概述
为了达到标识的目的,许多应用程序需要生成唯一编号,比如:商品编号、交易流水号等。MySQL数据库同样能够支持这样的需求场景,AUTO_INCREMENT就是为MySQL实现序列的方式,它会自动生成序列编号。但是它的使用是有要求的,比如:
- 每个表只能有一个列具备AUTO_INCREMENT属性,并且为整数型
- AUTO_INCREMENT列不能包含NULL值(MySQL会自动设置为NOT NULL)
- AUTO_INCREMENT列上必选要有索引,常见为primary key和unique index
备注:由于存储引擎的不同对于序列的定义和使用存在差异,本文以innodb引擎作为讲解,具体差异区别,可参考后期分享:《浅析MySQL存储引擎序列属性》。
二、场景演示
设置为AUTO_INCREMENT属性后,每一次插入数据都会向前增加一位数,但是如果删除行后,序列会怎么样呢?
mysql> CREATE TABLE animals (
-> id MEDIUMINT NOT NULL AUTO_INCREMENT,
-> name CHAR(30) NOT NULL,
-> PRIMARY KEY (id)
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> INSERT INTO animals (name) VALUES
-> ('dog'),('cat'),('penguin'),
-> ('lax'),('whale'),('ostrich');
Query OK, 6 rows affected (0.01 sec)
Records: 6 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM animals;
+----+---------+
| id | name |
+----+---------+
| 1 | dog |
| 2 | cat |
| 3 | penguin |
| 4 | lax |
| 5 | whale |
| 6 | ostrich |
+----+---------+
6 rows in set (0.00 sec)
对于动物编号来说,序列的作用确实很好用,但是当删除某行数据后,序列会发生什么情况呢?
Query OK, 3 rows affected (0.02 sec)
mysql> SELECT * FROM animals;
+----+---------+
| id | name |
+----+---------+
| 1 | dog |
| 3 | penguin |
| 5 | whale |
+----+---------+
3 rows in set (0.00 sec)
现在序列(id)发生了断层。
Query OK, 1 row affected (0.01 sec)
mysql> SELECT * FROM animals;
+----+---------+
| id | name |
+----+---------+
| 1 | dog |
| 3 | penguin |
| 5 | whale |
| 7 | Horse |
+----+---------+
4 rows in set (0.00 sec)
mysql> INSERT INTO animals (name) VALUES ('Kangaroo');
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM animals;
+----+----------+
| id | name |
+----+----------+
| 1 | dog |
| 3 | penguin |
| 5 | whale |
| 7 | Horse |
| 8 | Kangaroo |
+----+----------+
5 rows in set (0.00 sec)
在插入新数据后,原来被删除的序列已经不再重复使用了,而下一个序列为未使用的最小整数。删除当前行对于下一次序列的分配,没有影响。
对于每次数据进行插入,都会从AUTO_INCREMENT列中获取最大值,在进行偏移量增加。如默认的偏移量为1。
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| auto_increment_increment | 1 |
+--------------------------+-------+
1 row in set (0.02 sec)
mysql> show variables like 'auto_increment_offset';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| auto_increment_offset | 1 |
+-----------------------+-------+
1 row in set (0.00 sec)
说明:对于最大值获取,不是简单使用max函数,这样并不准确,因为在并行事务中,可能会有其他会话进行插入更改,因此当前会话max值并不是准确的,同时还会存在自增长字段的值之间发生冲突,所以MySQL会调用LAST_INSERT_ID(),返回最新AUTO_INCREMENT最大值。
Query OK, 1 row affected (0.00 sec)
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 9 |
+------------------+
1 row in set (0.00 sec)
mysql> INSERT INTO animals (name) VALUES ('DEFG');
Query OK, 1 row affected (0.01 sec)
mysql> select * from animals where id =last_insert_id();
+----+------+
| id | name |
+----+------+
| 10 | DEFG |
+----+------+
1 row in set (0.00 sec)
对于并行事务,AUTO_INCREMENT 计数会怎么分配呢?
下面做个案例测试:
会话1:
mysql> set autocommit=0
-> ;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from animals;
+----+----------+
| id | name |
+----+----------+
| 1 | dog |
| 3 | penguin |
| 5 | whale |
| 7 | Horse |
| 8 | Kangaroo |
| 9 | ABC |
| 10 | DEFG |
+----+----------+
7 rows in set (0.00 sec)
mysql> INSERT INTO animals (name) VALUES ('LISAT1');
Query OK, 1 row affected (0.00 sec)
mysql> select * from animals;
+----+----------+
| id | name |
+----+----------+
| 1 | dog |
| 3 | penguin |
| 5 | whale |
| 7 | Horse |
| 8 | Kangaroo |
| 9 | ABC |
| 10 | DEFG |
| 11 | LISAT1 |
+----+----------+
8 rows in set (0.01 sec)
会话2:
mysql> set autocommit=0
-> ;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from animals;
+----+----------+
| id | name |
+----+----------+
| 1 | dog |
| 3 | penguin |
| 5 | whale |
| 7 | Horse |
| 8 | Kangaroo |
| 9 | ABC |
| 10 | DEFG |
+----+----------+
7 rows in set (0.00 sec)
mysql> INSERT INTO animals (name) VALUES ('LISAT2');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO animals (name) VALUES ('LISAT3');
Query OK, 1 row affected (0.00 sec)
mysql>
mysql> select * from animals;
+----+----------+
| id | name |
+----+----------+
| 1 | dog |
| 3 | penguin |
| 5 | whale |
| 7 | Horse |
| 8 | Kangaroo |
| 9 | ABC |
| 10 | DEFG |
| 12 | LISAT2 |
| 13 | LISAT3 |
+----+----------+
9 rows in set (0.00 sec)
会话1:
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
会话2:
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from animals;
+----+----------+
| id | name |
+----+----------+
| 1 | dog |
| 3 | penguin |
| 5 | whale |
| 7 | Horse |
| 8 | Kangaroo |
| 9 | ABC |
| 10 | DEFG |
| 12 | LISAT2 |
| 13 | LISAT3 |
+----+----------+
9 rows in set (0.00 sec)
可以从上面的测试看出,当会话1持有该序列后,会对该序列占有语句锁,会话2重新申请下一个序列,因此出现了序列不连续情况,这样的目的其实也是为了避免线程冲突,性能优先。
在高效使用AUTO_INCREMENT列时,有几项注意事项:
- 自增序列的目的是得到一系列的正整数序列,,因此不支持非正数使用。
- 可以将AUTO_INCREMENT列定义为UNSIGED类型,创建主键 UNSIGNED 和 AUTO_INCREMENT 连用 表示从0开始自增 (由0开始自增,所以第一个自增的id为 1 ) 但可以增加的范围为,不加 UNSIGNED 的两倍
- 使用truncate table来清除某个表的内容,可以将该表的序列重置为1开始。
- MySQL 教程
- MySQL 安装
- MySQL 管理与配置
- MySQL PHP 语法
- MySQL 连接
- MySQL 创建数据库
- MySQL 删除数据库
- MySQL 选择数据库
- MySQL 数据类型
- MySQL 创建数据表
- MySQL 删除数据表
- MySQL 插入数据
- MySQL 查询数据
- MySQL where 子句
- MySQL UPDATE 查询
- MySQL DELETE 语句
- MySQL LIKE 子句
- mysql order by
- Mysql Join的使用
- MySQL NULL 值处理
- MySQL 正则表达式
- MySQL 事务
- MySQL ALTER命令
- MySQL 索引
- MySQL 临时表
- MySQL 复制表
- 查看MySQL 元数据
- MySQL 序列 AUTO_INCREMENT
- MySQL 处理重复数据
- MySQL 及 SQL 注入
- MySQL 导出数据
- MySQL 导入数据
- MYSQL 函数大全
- MySQL Group By 实例讲解
- MySQL Max()函数实例讲解
- mysql count函数实例
- MYSQL UNION和UNION ALL实例
- MySQL IN 用法
- MySQL between and 实例讲解
- 轻松应对并发问题,Newbe.Claptrap 框架中 State 和 Event 应该如何理解?
- 如何暂停一个正在运行的线程?
- WebMonitor采集端优化之路
- 美颜算法之自动祛斑算法实现 | 案例分享
- 附025.kubeadm部署Kubernetes更新证书
- 消息提示时间的格式化例子(小程序)
- 【Spark】用scala2.11编译打包构建镜像
- 移动端事件穿透的原理与解决方案
- 你被追尾了
- 深入理解JavaScript作用域
- 《闲扯Redis七》Redis字典结构的底层实现
- 深入理解JavaScript闭包之什么是闭包
- 按需取余
- Cypress 获取table内容动态tr和td
- Java 并发编程之 ConcurrentHashMap 源码分析(小长文)