暂无图片
暂无图片
10
暂无图片
暂无图片
暂无图片

MySQL 5.7 升级 8.0 避坑指南:从一个“诡异”的索引重复案例说起

原创 金同学 2026-03-04
256

前言
在进行 MySQL 大版本升级时,我们往往关注语法兼容性、参数变更或性能优化,却容易忽略一个隐藏在底层的“隐形杀手”——排序规则(Collation)
最近在一次从 MySQL 5.7 到 8.0 的升级迁移中,我们遭遇了一个看似“诡异”的数据问题:原本在 5.7 上能正常插入的两条“看似不同”的数据,在 8.0 上却触发了唯一索引冲突。
本文将复现这一场景,深入分析原因,并提供可落地的解决方案。

一、问题复现:两条数据,为何在 8.0 中“撞车”?

我们构建一个简单的测试表,仅包含一个唯一键 col1:

CREATE TABLE `mytest` ( `id` varchar(20) DEFAULT NULL, `col1` varchar(20) DEFAULT NULL, UNIQUE KEY `uniq_col1` (`col1`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

在 MySQL 5.7 环境中:
我们插入两条数据:

  • testⅠ(罗马数字 Ⅰ)
  • testI(英文字母 I)

执行结果:

mysql> insert into mytest values(1,'testⅠ'); Query OK, 1 row affected mysql> insert into mytest values(1,'testI'); Query OK, 1 row affected mysql> select * from mytest; +------+---------+ | id | col1 | +------+---------+ | 1 | testⅠ | | 1 | testI | +------+---------+ 2 rows in set

插入成功,两条数据共存,且比较 ‘Ⅰ’ = ‘I’ 返回 0(不相等)。

在 MySQL 8.0.39 环境中:
同样的建表语句(注意:MySQL 8.0 默认使用 utf8mb4_0900_ai_ci 排序规则):

mysql> insert into mytest values(1,'testⅠ'); Query OK, 1 row affected mysql> insert into mytest values(1,'testI'); ERROR 1062 (23000): Duplicate entry 'testI' for key 'mytest.uniq_col1'

插入失败!系统认为 ‘testⅠ’ 和 ‘testI’ 是重复的!

进一步验证:

mysql> select 'Ⅰ' = 'I'; +-------------+ | 'Ⅰ' = 'I' | +-------------+ | 1 | +-------------+ 1 row in set

在 8.0 中,罗马数字 Ⅰ 和字母 I 被视为完全相同!

二、根本原因:排序规则(Collation)的演进

MySQL 5.7 和 8.0 对 utf8mb4 字符集的默认排序规则不同:

版本 默认 Collation 特点
MySQL 5.7 utf8mb4_general_ci 简单粗暴,按字节比较,区分度低
MySQL 8.0 utf8mb4_0900_ai_ci 基于 Unicode 9.0 标准,支持语言敏感、重音不敏感、大小写不敏感,智能合并相似字符

关键点在于:utf8mb4_0900_ai_ci 将罗马数字 Ⅰ、Ⅱ、Ⅲ 等归一化为英文字母 I、II、III,因此在唯一索引中被视为重复。这不仅是罗马数字的问题,还包括:

  • 全角/半角字符(如 ( vs ()
  • 重音字符(如 é vs e)
  • 特殊符号(如 ·、-、. 等在某些排序规则下可能被忽略或归一化)

三、影响范围与风险

  • 这种行为变更在升级过程中可能引发严重问题:
  • 数据丢失或插入失败:原本合法的数据在新库中被拒绝。
  • 唯一索引失效:原本唯一的键在新排序规则下重复,导致索引无法建立。
  • 应用逻辑异常:依赖字符串比较的业务逻辑(如用户登录、订单去重)可能出错。
  • 迁移失败:使用 mysqldump 或数据同步工具时,可能在目标库报唯一键冲突。

四、解决方案:如何安全升级?

方案一:保持排序规则一致(推荐用于平滑迁移)

在升级前,显式指定表和字段的排序规则为 utf8mb4_general_ci,避免默认使用 utf8mb4_0900_ai_ci。

-- 建表时指定 CREATE TABLE `mytest` ( `id` varchar(20) DEFAULT NULL, `col1` varchar(20) DEFAULT NULL, UNIQUE KEY `uniq_col1` (`col1`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

或修改已有表:

ALTER TABLE `mytest` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

方案二:应用层清洗数据(适用于新系统)

如果希望利用 8.0 的智能排序能力,需在应用层对输入数据进行标准化处理,可使用正则表达式检测非标准字符。

-- 我在生产系统迁移中,使用下面语句可以识别出非标字符,可借鉴、补充。 SELECT * FROM mytest WHERE col1 REGEXP '[^a-zA-Z0-9_·.-\\u4e00-\\u9fa5]';

五、总结

MySQL 8.0 的 utf8mb4_0900_ai_ci 排序规则更智能、更符合国际化需求,但这种“智能”可能打破旧系统的数据假设。
在从 5.7 升级到 8.0 时,不要依赖默认排序规则,应主动评估并调整字符集与排序规则,避免因“看似相同”的字符导致数据不一致或业务中断。

下面是一些升级建议:

  • 升级前全量扫描:使用正则或脚本扫描所有表中可能受排序规则影响的字段。
  • 测试环境模拟:在测试库中使用 utf8mb4_0900_ai_ci 进行全量数据导入测试。
  • 索引重建策略:升级后,对唯一索引进行 CHECK TABLE 或 OPTIMIZE TABLE。
  • 应用层兼容性测试:重点测试用户注册、登录、订单创建等依赖字符串比较的流程。
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论