MySQL的utf8不是真正的UTF-8,仅支持3字节字符;utf8mb4才是完整实现,需显式指定字符集、修正字段定义、同步客户端连接,并合理选择排序规则。

utf8 在 MySQL 里根本不是真正的 UTF-8
MySQL 的 utf8 字符集实际只支持最多 3 字节的 Unicode 字符,也就是 U+0000 到 U+FFFF 范围,不包含 emoji、部分中文生僻字(如“?”)、古汉字、数学符号等 4 字节字符。它只是个历史遗留命名错误,官方也承认这是个“误称”。utf8mb4 才是 MySQL 中真正完整的 UTF-8 实现,支持全部 Unicode 字符(U+0000–U+10FFFF)。
建表或改表时必须显式指定 utf8mb4
即使服务器默认字符集设为 utf8mb4,单个表、列仍可能沿用旧配置。常见遗漏点:
-
CREATE TABLE语句中未加CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci - 修改已有表时只改了表级字符集,漏掉
MODIFY COLUMN或CHANGE COLUMN对字段重定义 - 索引字段长度超限:
VARCHAR(255)在utf8mb4下,若用utf8mb4_unicode_ci排序规则,索引长度按每字符 4 字节算,可能突破 InnoDB 单索引 767 字节限制(尤其在innodb_large_prefix=OFF时)
正确做法示例:
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
但注意:这仅更新表和字段默认值,不改变已存在字段的定义;如需确保字段本身也生效,还需单独执行:
ALTER TABLE users MODIFY COLUMN name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
客户端连接必须同步设置 utf8mb4
只改服务端字符集没用,如果连接层(如 JDBC、PHP PDO、Python mysqlclient)没声明 charset=utf8mb4,数据写入/读取时仍会按 utf8 解码,导致乱码或截断。典型错误现象:
- 插入 emoji 报错
Incorrect string value: '\xF0\x9F\x98\x80'... - 查出来字段显示为
???或空字符串 - 同一字段,命令行
mysql客户端能正常显示,应用却乱码
检查连接字符集是否生效:
SELECT @@character_set_client, @@character_set_connection, @@character_set_results;
三者都应返回 utf8mb4。否则需在连接字符串中显式指定,例如:
- JDBC:
?useUnicode=true&characterEncoding=utf8mb4 - PHP PDO:
charset=utf8mb4加入 DSN - my.cnf 全局配置(仅对未显式覆盖的连接有效):
default-character-set = utf8mb4([client] 段)和collation-server = utf8mb4_unicode_ci([mysqld] 段)
utf8mb4_unicode_ci 和 utf8mb4_0900_as_cs 的选择
排序规则影响大小写敏感性、重音处理和性能。MySQL 8.0+ 默认推荐 utf8mb4_0900_as_cs(accent-sensitive + case-sensitive),而 utf8mb4_unicode_ci 是较老的、不区分大小写的通用规则。
- 需要精确匹配(如密码盐、token、唯一键校验)→ 选
_as_cs规则 - 用户昵称、文章标题等需模糊搜索 →
utf8mb4_unicode_ci或更现代的utf8mb4_0900_ai_ci(accent-insensitive)更合适 - 已有系统升级时注意:更换 collation 可能导致唯一索引冲突(比如原来 “A” 和 “a” 被视为不同值,新规则下视为相同)
查看当前支持的规则:
SHOW COLLATION WHERE Charset = 'utf8mb4' AND Collation LIKE '%_ci' OR Collation LIKE '%_cs';真正麻烦的是存量系统改造——字段改了、表改了、连接改了,但某个老旧的定时脚本或第三方工具还在用
utf8 连接,一跑就出错。这种地方往往藏得深,得靠日志里的报错字符和具体 SQL 上下文才能揪出来。










