utf8 与 utf8mb4:emoji 存不进去的原因
发布时间:2026-10-07 16:57       
更新时间:2026-10-08 11:24
emoji 存进 MySQL 变成一串问号,根因一句话:MySQL 的 utf8 不是真正的 UTF-8,每字符最多三个字节,而 emoji 需要四个字节。补齐字节宽度的 utf8mb4 才是解法。
## 问:历史原因是什么
MySQL 早年实现 utf8 时,按"每字符最多三字节"做了个简配版,当时够用。后来 Unicode 字符扩展到三字节以外,emoji 和部分生僻字都需要四字节,MySQL 补了 utf8mb4(most bytes 4,最多四字节)来兜底,但为了兼容老库,utf8 这个名字保留至今。官方现在明确建议新库一律用 utf8mb4。判断现状用 SHOW VARIABLES LIKE 'character%',server、database、client 几层各有各的值,逐层核对。
## 问:要改哪几处
库、表、连接三处必须都是 utf8mb4,断一处就会出问题:
```
ALTER DATABASE appdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE comments CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
连接侧在应用连上后执行 SET NAMES utf8mb4,或在连接串参数里指定字符集,保证客户端到服务端全程四字节通行。中间有代理或中间件层的,同样要核对其字符集配置。CONVERT 会重写全表数据,大表放低峰执行并预留维护窗口,改完抽查几条含特殊字符的记录。老库不要急着一夜切换,按库按表分批迁,迁一张验一张。
## 问:改完为什么报索引太长
VARCHAR 列换成 utf8mb4 后每字符占四字节,索引长度可能超过限制,报 Specified key was too long。处理办法是缩短索引前缀长度,或确认表使用 DYNAMIC 行格式(5.7 之后默认),前缀上限可到 3072 字节,一般不再受限。前缀索引的写法是列名后跟括号长度,比如 col(20),取前若干字符建索引,代价是区分度略降。
## 问:排序规则怎么选
utf8mb4_general_ci 速度快但比较粗糙;utf8mb4_unicode_ci 更符合 Unicode 规则;MySQL 8.0 默认的 utf8mb4_0900_ai_ci 更新更准。选哪个都可以,关键是全站统一,两套排序规则混用的直接后果是 JOIN 两边字符集不一致,索引失效,查询突然变慢。新建表不指定排序规则时继承库的默认值,把库的默认值定对,后面全省事。
新建库把默认字符集直接定为 utf8mb4,做在前面比事后 CONVERT 全表轻松得多。老库迁移前,先在测试环境把三处改动完整演练一遍。