当我们向一个已存在的 MySQL 表中添加新字段时,有时会遇到 1062 错误:

1
1062 - Duplicate entry 'xxxx' for key 'idx_unique'

这个报错的含义是在一个唯一索引(unique index)下有重复的值。

MySQL 官方文档《InnoDB 在线 DDL 限制》有一段关于该错误的说明,原文内容如下:

When running an online DDL operation, the thread that runs the [ALTER TABLE](https://dev.mysql.com/doc/refman/5.7/en/alter-table.html) statement applies an online log of DML operations that were run concurrently on the same table from other connection threads. When the DML operations are applied, it is possible to encounter a duplicate key entry error (ERROR 1062 (23000): Duplicate entry), even if the duplicate entry is only temporary and would be reverted by a later entry in the online log. This is similar to the idea of a foreign key constraint check in InnoDB in which constraints must hold during a transaction.

中文大概意思是:

当执行像 ALTER TABLE 等 DDL 语句时,会记录同时期对该表的 DML 操作。执行 DDL 语句时,会应用这些 DML 操作。如果其中的某个 DML 操作导致了重复键错误(即 ERROR 1062 (23000): Duplicate entry),那么 DDL 语句也会失败,即使该重复键只是暂时的,最终会被后续 DML 操作撤消。这类似于 InnoDB 数据库的外键约束检查,一个事务内部,外键约束必须被满足。

举一个例子,我的生产环境 MySQL 版本是 5.7.31,表 tb_post 的存储引擎是 InnoDB,主键是 id,唯一索引由 post_idchanneldt 三个字段组成,具体建表语句如下:

1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE `tb_post` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`post_id` varchar(127) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'POST ID',
`channel` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '渠道',
`dt` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '日期',
`pv_tt` int(11) DEFAULT NULL COMMENT '今日pv',
`uv_tt` int(11) DEFAULT NULL COMMENT '今日访问uv',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_unique` (`post_id`,`channel`,`dt`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='POST每日指标'

该表的数据指标——今日 uv、pv 由 Flink 计算后写入,写操作频繁。

现在该表需要新增用户点赞数量的字段 like_tt,执行 SQL:

1
2
ALTER TABLE tb_post 
ADD COLUMN like_tt BIGINT NOT NULL DEFAULT 0 COMMENT '今日点赞数量'

结果报错,错误信息提示如下:

1
1062 - Duplicate entry 'poeG8yefW3XwxK6zvNy-APP-2023-07-05' for key 'idx_unique'

网上的解决方法有多种,其中包括:

  • 临时删除该唯一索引,添加字段后,再恢复唯一索引。
  • 给表加表锁。

虽然通过以上方法能做到给表新增字段,但或多或少都会给现有业务造成影响,如出现重复数据记录、数据写不进等。

为尽量少地影响业务,最好的解决方法是通过在 SQL 的末尾加上 ALGORITHM=COPY

1
2
ALTER TABLE tb_post 
ADD COLUMN like_tt BIGINT NOT NULL DEFAULT 0 COMMENT '今日点赞数量', ALGORITHM=COPY

(END)