首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >为 MySQL 加字段,我竟然遇史诗级 Bug?

为 MySQL 加字段,我竟然遇史诗级 Bug?

作者头像
爱可生开源社区
发布于 2025-06-12 13:14:33
发布于 2025-06-12 13:14:33
9690
举报

作者:天勿雨,DBA 工程师。

爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。

本文约 1200 字,预计阅读需要 3 分钟。


图片
图片

1. 发生了什么?

某天开发火急火燎找来,说是给表加字段时,出现 ERROR 1062 (23000):Duplicate entry …… key …… 报错,怀疑是 MySQL 出了问题。我当场就掏出 50m 大刀指着他:”别什么事都赖数据库头上!“

当然这只是牛马打工人的幻想,活是要干的,态度也是要诚恳的。以我的数据库运维经验来看 SO EASY~,这不妥妥就是加了个带唯一约束的字段,字段里面有重复数据导致的报错么。我正准备大肆谴责开发,字打完一半才发现逻辑有些不通,

疑点:如果是新增字段,怎么会报数据重复(又不是给字段加唯一索引)?

保险起见,我默默删除了文字,重新编辑:”亲~数据库连接信息,执行的 SQL,具体报错提供下哦~“。待我拿到了具体的信息,复现了这个奇怪的报错,有图有真相。

生产环境能给大家看的就只有这么多了,但相信对各位 “彦祖” 来说足够看出问题了:

  1. 这就是个普通的加字段操作
  2. 加的字段和报重复值的字段不一样

但对我来说,乍一看就只看到了 duplicate key,第一反应就是看该字段是否有重复值。排查后,发现这个值是唯一的……

众人看我陷入了思考,以为我已经有了解决方法,只是在看使用哪种更加简单有效,殊不知我已经把离职报告写好了。我一个刚入门的懂什么数据库啊?

玩笑归玩笑,言归正传,遇事不决,掏出新手的三板斧:CSDN,AI,Google。

2. 原因是什么?

摸索一番后,总算找到了原因,首先我们得了解下 Online DDL 的大致原理:

MySQL 在执行 DDL 期间,会将发生的 DML 存储到临时日志文件里,这个临时日志文件大小由配置参数innodb_online_alter_log_max_size[1] 指定。等 DDL 执行完后,再把 DML 应用到对应的表上。

随后我又找到了对应的 Bug 记录[2]。官方解释这并不算是 Bug,而是 Online DDL 的特性。

简单说:MySQL 在执行 DDL 时,如果有两条 DML,且插入了同样的数据,就可能会有 duplicate entry 报错。

3. 把问题复现一下

看来是我的无知误会了MySQL,虽然官网解释的很清楚,但咱们还是得来验证下。

3.1 生成如下表结构

必须得要有主键和唯一键。

代码语言:javascript
复制
mysql> show create table sbtest1\G
*************************** 1.row ***************************
       Table: sbtest1
Create Table: CREATE TABLE`sbtest1` (
`id`int() NOT NULL AUTO_INCREMENT,
`k`int() NOT NULL DEFAULT '0',
`c`char() COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`pad`char() COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
UNIQUEKEY`pad` (`pad`),
KEY`k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
row in set (0.00 sec)

3.2 查看表数据

代码语言:javascript
复制
mysql> select * from sbtest1 limit ;
+----+---------+-------------------------------------------------------------------------------------------------------------------------+-----+
| id | k       | c                                                                                                                       | pad |
+----+---------+-------------------------------------------------------------------------------------------------------------------------+-----+
|  1 | 2507307 | 68487932199-96439406143-93774651418-41631865787-96406072701-20604855487-25459966574-28203206787-41238978918-19503783441 | h   |
|  2 | 2512400 | 13241531885-45658403807-79170748828-69419634012-13605813761-77983377181-01582588137-21344716829-87370944992-02457486289 | g   |
+----+---------+-------------------------------------------------------------------------------------------------------------------------+-----+

3.3 添加字段

代码语言:javascript
复制
mysql> alter table sbtest1 add column name varchar();

3.4 插入数据,制造唯一键冲突报错

代码语言:javascript
复制
mysql> insert into sbtest1(k,pad) values(,'h');
ERROR 1062 (23000): Duplicate entry 'h' for key 'pad'

3.5 随后添加字段的 DDL 报错

代码语言:javascript
复制
mysql> alter table sbtest1 add column name varchar();
ERROR 1062 (23000): Duplicate entry 'h' for key 'pad'

3.6 发生疑惑

令人疑惑的是,如果主键冲突,却不会导致 DDL 报错。如果只存在唯一键而无主键,也不会导致 DDL 报错。

代码语言:javascript
复制
# 新增字段
mysql> alter table sbtest1 add column home varchar();

# 插入数据
mysql> insert into sbtest1(id,k) values(,) on duplicate key updateid=;
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'

# 新增字段不会报错
mysql> alter table sbtest1 add column home varchar();
Query OK, 0 rows affected (1 min 2.57 sec)
Records: 0  Duplicates: 0  Warnings: 0

4. 总结

从上文大家可知,MySQL 执行 DDL 报 duplicate entry 的条件及原因,我这边再给大家总结:

条件

  1. 表结构里面必须得是主键 + 唯一键。
  2. 在执行 DDL 的时候,插入和唯一键冲突的值。

原因

MySQL Online DDL 的特性,在执行 DDL 时,MySQL 会把 DML 存储在日志中,等 DDL 执行完后再应用 DML,如果多条 DML 会产生数据冲突,在 DDL 完成后,应用 DML 时,理所当然会出现 duplicate entry 的错误了。

6. 最后的疑问

为什么一定得是主键 + 唯一键的表结构,唯一键冲突才会导致这个问题?

参考资料

[1] 

innodb_online_alter_log_max_size: https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_online_alter_log_max_size

[2] 

bug#76895: https://bugs.mysql.com/bug.php?id=76895

本文关键字:#MySQL #DDL #报错

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-06-11,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • 1. 发生了什么?
  • 2. 原因是什么?
  • 3. 把问题复现一下
    • 3.1 生成如下表结构
    • 3.2 查看表数据
    • 3.3 添加字段
    • 3.4 插入数据,制造唯一键冲突报错
    • 3.5 随后添加字段的 DDL 报错
    • 3.6 发生疑惑
  • 4. 总结
    • 条件
    • 原因
  • 6. 最后的疑问
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档