mysql 自增 空洞_MySQL MySQL 案例:自增列的空洞问题与“小”BUG _好机友

前言

在 MySQL 的常见规范里面,每个表都要设置主键,一般来说都会推荐自增列作为主键,这和 MySQL 属于聚簇索引表有关,顺序增长的主键比较合适。而自增列中比较常遇见的问题就是自增列的空洞。原生的 MySQL 自增列也存在一个 BUG,可能会影响到数据一致性,本文也会详细介绍,在自建 MySQL 的时候尽量不要踩到这个坑。

空洞问题

问题介绍

自增列的空洞一般指的就是自增列不是连续增长,中间出现一些数值上的断层。比如手动写入自增列的值,就有可能会出现这种现象:mysql> insert into t1 values(1,1),(3,3),(5,5); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> mysql> mysql> select * from t1; +----+------+ | id | a | +----+------+ | 1 | 1 | | 3 | 3 | | 5 | 5 | +----+------+ 3 rows in set (0.00 sec) mysql> show create table t1; +-------+---------------------------------------------------------------------------+ | Table | Create Table | +-------+---------------------------------------------------------------------------+ | t1 | CREATE TABLE `t1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `a` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 | +-------+---------------------------------------------------------------------------+ 1 row in set (0.00 sec)

可以看到表只有三行数据,但是自增列的值已经变到了 6,“浪费”了两个值。当浪费的值过多的时候,就可能会出现如下的问题:mysql> insert into t1 values(2147483647,0); Query OK, 1 row affected (0.01 sec) mysql> show create table t1; +-------+--------------------------------------------------------------------------+ | Table | Create Table | +-------+--------------------------------------------------------------------------+ | t1 | CREATE TABLE `t1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `a` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2147483647 DEFAULT CHARSET=utf8mb4 | +-------+--------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> insert into t1 values(null,0); ERROR 1062 (23000): Duplicate entry '2147483647' for key 'PRIMARY' mysql>

自增列如果浪费过多,即使是 int 也很可能会用完,然后再尝试插入数据的时候就会报错了。

问题的监控

自增列的监控,在 5.7 版本(低于 5.7 的还是尽快升级上来吧)是比较方便的,可以在 sys 视图中直接查询视图mysql> select * from sys.schema_auto_increment_columns\G *************************** 1. row *************************** table_schema: test table_name: t1 column_name: id data_type: int column_type: int(11) is_signed: 1 is_unsigned: 0 max_value: 2147483647 auto_increment: 2147483647 auto_increment_ratio: 1.0000 *************************** 2. row *************************** table_schema: sbtest table_name: sbtest1 column_name: id data_type: int column_type: int(11) is_signed: 1 is_unsigned: 0 max_value: 2147483647 auto_increment: 10000001 auto_increment_ratio: 0.0047 2 rows in set (0.04 sec) mysql>

通过比较 max_value,auto_increment,auto_increment_ratio 这三个值很容就能监控到即将出现问题的自增列。真的可能会出现问题的时候,就需要及时在业务维护窗口来调整自增列的数据类型,使用 unsigned 或者是 bigint。

问题的避免

如何避免这个问题,涉及到 MySQL 业务实际使用的 insert 语句的类型,这里推荐阅读(官方文档),里面有详细的分析和场景解析。

简单来说,自增列的值有一个特点:一旦被使用之后,是不会被 rollback 的,因此当各类 insert 操作被回滚之后,自增列的值就被“浪费了”。实际上这个 rollback 指代的不仅仅是回滚,而是指那些拿到了自增列值,但是没有实际 insert 数据的场景。

因此除了 rollback 事务的场景以外,需要特别关注的就是 REPLACE 和 INSERT…ON DUPLICATE KEY UPDATE,因为这两个操作会获取自增列的值,但是经常不会触发 insert,而是 update。

BUG

关于这个 BUG,其实也不好说是 BUG 还是“特性”。现象是 MySQL 在 5.7 和 5.7 之前,自增列的值是保存在内存中的,这就导致了 MySQL 重启之后会丢失这个自增列的值,所以每次重启之后,MySQL 会把表的自增列的值重置为自增列的 MAX VALUE。实践一下看看效果:mysql> truncate table t1; Query OK, 0 rows affected (0.03 sec) mysql> insert into t1 values(1,1),(3,3),(5,5),(7,7); Query OK, 4 rows affected (0.01 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> show create table test.t1; +-------+----------------------------------------------------------------------------+ | Table | Create Table | +-------+----------------------------------------------------------------------------+ | t1 | CREATE TABLE `t1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `a` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 | +-------+---------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> delete from t1 where id = 7; Query OK, 1 row affected (0.01 sec) mysql> show create table test.t1; +-------+--------------------------------------------------------------------------+ | Table | Create Table | +-------+--------------------------------------------------------------------------+ | t1 | CREATE TABLE `t1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `a` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 | +-------+--------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> exit Bye root@VM-56-136-debian:~# service mysql restart root@VM-56-136-debian:~# mysqllocal mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 2 Server version: 5.7.31-log MySQL Community Server (GPL) Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> show create table test.t1; +-------+---------------------------------------------------------------------------+ | Table | Create Table | +-------+---------------------------------------------------------------------------+ | t1 | CREATE TABLE `t1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `a` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 | +-------+---------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql>

可以看到原生的 MySQL 在重启之后自增列的值被重置了。这个现象被称之为 BUG 主要是在现在的互联网业务中,支撑业务数据的不仅仅只有 MySQL,还可能会有 Redis,RabbitMQ 等缓存和消息队列或者是单独的 MySQL 日志归档库,自增列可能会被用来作为关联各个存储之间的“逻辑外键”,当 MySQL 重启之后,新写入的数据可能会用到已经被删除的值,导致数据库中的数据和外部系统之间的数据出现错误的关联。另外一种问题场景就是 MySQL 自身各个表之间有外键关系,但是没有建立外键约束,也会遇到类似的问题。

这个 BUG 在 MySQL 8.0 修复了,在 8.0 中,MySQL 把这个值进行了持久化,重启之后也不会被重置。当然,数据库 MySQL 也修复了这个问题,可以放心使用~

总结一下

自增列虽然已经长时间,大范围的使用过了,但是也要注意这类技术可能会存在一些隐藏的问题,防微杜渐,保障业务安全运行是 DBA 应尽的职责。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值