字符集与排序规则:utf8mb4与乱码问题、排序规则影响查询结果——经验分享/踩坑向

阿乐
阿乐 管理员 黑卡会员
发布于 2026-09-12 14:57 ·2 浏览 ·0 回复

字符集和排序规则这两件事,平时没人注意,一旦出问题就是“玄学”:明明存进去的中文,读出来变成问号;明明查的是 `ABC`,结果 `abc` 也匹配上了;更离谱的是,同一张表换个排序规则,唯一索引突然冲突。最近又踩了一轮,把经验整理一下,希望能帮你少加几个班。

从一次乱码排查说起

某次线上反馈:用户昵称里的 emoji 存不进去,接口直接报 `Incorrect string value: '\xF0\x9F...'`。第一反应是表字符集不对,一看建表语句:`CHARSET=utf8`。问题就出在这——MySQL 里的 `utf8` 并不是真正的 UTF-8,它最多只支持 3 字节,而 emoji、部分生僻字是 4 字节。`utf8mb4` 才是完整的 UTF-8 实现,所以现在新建库表,无脑选 `utf8mb4` 基本是标配。

但改成 `utf8mb4` 就万事大吉了吗?并没有。乱码问题往往是“链路”问题:客户端连接字符集、服务端默认字符集、表字符集、字段字符集,任何一环不一致都可能出幺蛾子。比如表已经是 `utf8mb4`,但连接串没加 `characterEncoding=utf8`,或者服务端 `character_set_client` 还是 `latin1`,写入时照样乱。排查时可以用 `SHOW VARIABLES LIKE 'character_set%';` 和 `SHOW VARIABLES LIKE 'collation%';` 看一眼,重点确认 `character_set_client`、`character_set_connection`、`character_set_results` 是不是 `utf8mb4`。连接层统一用 `SET NAMES utf8mb4;` 或 JDBC 的 `useUnicode=true&characterEncoding=utf8`,能省掉很多“为什么存进去是问号”的困惑。

排序规则不只是“大小写敏感”

字符集决定“能不能存”,排序规则决定“怎么比较”。很多人只记得 `_ci` 是大小写不敏感,`_bin` 是二进制比较,但实际影响面比想象中大。

常见排序规则:
- `utf8mb4_general_ci`:老默认,比较快,但 Unicode 排序规则不够严谨;
- `utf8mb4_unicode_ci`:基于 Unicode Collation Algorithm,相对更准确,MySQL 5.7 常用;
- `utf8mb4_0900_ai_ci`:MySQL 8.0 默认,`ai` 表示口音不敏感,`ci` 表示大小写不敏感;
- `utf8mb4_bin`:按二进制比较,大小写、重音都敏感。

这些规则直接影响 `WHERE`、`ORDER BY`、`GROUP BY`、`JOIN`,甚至唯一索引。踩过最狠的一次:某业务用邀请码做唯一索引,字段排序规则是 `utf8mb4_general_ci`,结果用户输入 `aBc` 和 `AbC` 被判定为重复,插入失败。业务上邀请码本该区分大小写,这就是排序规则选错导致的“查询结果不符合预期”。反过来,如果邮箱唯一索引用 `_bin`,`Test@xx.com` 和 `test@xx.com` 就能同时存在,但大多数业务又希望它们视为同一个邮箱。所以选 `ci` 还是 `bin`,不是技术偏好,而是业务语义。

排序规则不一致引发的隐式转换

更隐蔽的坑是 JOIN。两张表关联字段的排序规则不同,比如一张是 `utf8mb4_general_ci`,另一张是 `utf8mb4_unicode_ci`,执行 JOIN 时可能直接报:

`Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (utf8mb4_unicode_ci,IMPLICIT) for operation '='`

即使不报错,也可能因为隐式转换导致索引失效,查询从毫秒变秒级。解决办法要么统一排序规则,要么在 SQL 里显式 `COLLATE`,但后者容易让索引失效,最好还是从建表规范上统一。

另外,`ORDER BY` 也受排序规则影响。中文按 `general_ci` 排出来的顺序,和按拼音排完全不是一回事。如果业务要求中文按拼音排序,MySQL 层面并不好直接满足,通常得靠应用层或额外排序字段。

一些实用的避坑建议

1. 新库新表统一 `utf8mb4`,MySQL 8.0 默认 `utf8mb4_0900_ai_ci`,5.7 建议 `utf8mb4_unicode_ci`。
2. 连接层显式设置字符集,别依赖服务端默认值。
3. 唯一索引、登录名、邀请码、订单号这类字段,先想清楚是否区分大小写,再决定用 `_ci`、`_cs` 还是 `_bin`。
4. JOIN 字段的字符集和排序规则保持一致。
5. 迁移时用 `ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;`,注意索引长度限制,必要时调整 `varchar` 长度或开启 `innodb_large_prefix`。

字符集和排序规则就像数据库里的“地基”,平时看不见,塌了才后悔。记住一句话:字符集管存储,排序规则管比较;统一 utf8mb4 是底线,按业务选 collation 才是进阶。下次再遇到乱码或者“查询结果不对”,先别怀疑人生,查查这两个参数,大概率能找到答案。

本文转载自 阿乐技术社区,原文地址:https://www.leleweb.cn/thread-268.html
转载请注明出处,版权归原作者所有。

全部回复 0

还没有回复,来抢沙发~