MySQL中文乱码根本原因是character_set_client、connection、results三者未统一为utf8mb4,任一非utf8mb4都会导致中文在传输链路中被错解;必须通过SHOW VARIABLES确认三值全为utf8mb4,并在my.cnf中同步配置[client]、[mysql]、[mysqld]三段后重启服务。

MySQL中文数据出现乱码,根本不是“数据库存错了”,而是字符在传输链路上被错解了——character_set_client、character_set_connection、character_set_results这三个连接层变量只要有一个不是utf8mb4,中文就会在发送、解析或返回任一环节被截断、误读或丢字节。
查清当前会话的三变量是否一致
乱码发生时,第一件事不是改表、不是导数据,而是确认你此刻这条连接的实际编码行为。执行:
SHOW VARIABLES LIKE 'character\_set%';
重点看这三项值是否全为utf8mb4:
-
character_set_client:你发过去的 SQL 字符串(含中文)按什么编码解释 -
character_set_connection:MySQL 内部转换用的中间编码,必须和 client 严格一致 -
character_set_results:MySQL 返回结果时声明的编码,客户端必须按此解码
哪怕character_set_database是utf8mb4,只要上面三者中有一个是latin1或gbk,插入“你好”就可能变成“æ\x88\x91好”,查询出来就是一堆问号或 Mojibake。
为什么utf8不行,必须用utf8mb4
MySQL 的 utf8 是个历史包袱,实际叫 utf8mb3,最多只支持 3 字节字符。所有标准汉字虽能存,但微信昵称、emoji(如?)、生僻字(如“?”)会直接被截断或报错。
更隐蔽的问题是:即使你没存 emoji,某些 ORM 或驱动在初始化连接时若默认用 utf8 建连,character_set_client 就会被设成 utf8,后续所有中文都按 3 字节逻辑处理,一旦遇到需 4 字节的场景(比如某些输入法生成的组合字符),就出乱码。
所以:SET NAMES utf8 是危险操作;SET NAMES utf8mb4 才是安全底线。
配置文件里只写[mysqld]段没用
很多人在 my.cnf 里只配了:
[mysqld]<br>character-set-server = utf8mb4
这只能影响新建库/表的默认字符集,完全不控制客户端怎么连进来。
必须同时配齐三段:
-
[client]:控制所有客户端工具(mysql、mysqldump)的默认字符集 -
[mysql]:专用于mysql命令行客户端,优先级高于[client] -
[mysqld]:服务端行为兜底
改完必须重启:systemctl restart mysqld,reload 不生效。
ALTER TABLE CONVERT TO 要防索引超长
已有表从 utf8 升级到 utf8mb4,执行:
ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
会重建整张表,并把所有 VARCHAR 字段按 4 字节重算长度。InnoDB 旧版本单列索引上限是 767 字节,VARCHAR(255) 在 utf8mb4 下最多占 1020 字节(255 × 4),直接触发 Specified key was too long 错误。
解决方法不是删字段,而是先缩小长度或改用前缀索引,例如:
ALTER TABLE t MODIFY COLUMN name VARCHAR(191) CHARACTER SET utf8mb4;
因为 191 × 4 = 764
真正容易被忽略的是:乱码问题从来不是单一配置项能解决的,它横跨客户端、连接协议、服务端、存储引擎四层。哪怕你把表、库、服务器全设成 utf8mb4,只要应用连接时没传 charset=utf8mb4 参数,或者终端本身编码是 GBK,照样显示乱码。


















