根本原因是客户端与服务器字符集协商失败,必须统一character_set_client、character_set_connection、character_set_results为utf8mb4;需检查连接层三变量,强制连接时指定utf8mb4,并修改my.cnf中character-set-server=utf8mb4,重启服务后修复旧库表字符集。

MySQL查询结果出现中文乱码,根本原因不是“数据库存错了”,而是客户端与服务器之间字符集协商失败——character_set_client、character_set_connection、character_set_results 这三个变量不一致或未设为 utf8mb4,导致 MySQL 把你发过去的 UTF-8 字节当 Latin1 解,或者把返回的 UTF-8 字节用系统默认编码(如 GBK)去解。
查清当前连接层字符集三变量
执行这条命令,看实际生效的连接级设置:
SHOW VARIABLES LIKE 'character_set_%';
重点关注这三项:
-
character_set_client:客户端声明自己发送的 SQL 语句用什么编码(比如你用 Navicat 或命令行发 INSERT,它告诉 MySQL “我这段 SQL 是 utf8”) -
character_set_connection:MySQL 接收后内部转换用的中间编码(必须和 client 匹配,否则转义出错) -
character_set_results:MySQL 返回结果时,声明“我按这个编码给你数据”,客户端必须按此解码
如果其中任一值是 latin1 或 gbk,而你的应用/终端实际用的是 UTF-8,就必然乱码。不要只看 character_set_database,它只管存储,不管传输。
连接时强制指定 utf8mb4(最可靠的做法)
在建立连接的第一时间,就发一条 SET 命令覆盖默认协商逻辑。不同场景写法不同:
- 命令行客户端:
mysql -u root -p --default-character-set=utf8mb4启动,再进库后立即执行SET NAMES utf8mb4; - PHP PDO:
$pdo = new PDO($dsn, $user, $pass, [PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"]); - Python PyMySQL:
conn = pymysql.connect(..., charset='utf8mb4') - Java JDBC URL 加参数:
?useUnicode=true&characterEncoding=utf8mb4
SET NAMES utf8mb4 等价于同时设置 client/connection/results 为 utf8mb4,比单独 SET 三条更简洁安全。别用 utf8——那是 MySQL 的假 UTF-8,最多存 3 字节,微信昵称、emoji 全丢。
my.cnf / my.ini 里必须配对生效
仅改客户端连接不够,服务端默认行为也要兜底。在配置文件 [client] 和 [mysqld] 段都加:
[client] default-character-set = utf8mb4 [mysqld] character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci
注意:default-character-set 在 MySQL 5.7+ 已被弃用,但 [client] 段仍需保留,否则命令行客户端启动时不读 character-set-server;[mysqld] 段必须用 character-set-server,不能写 default-character-set,否则启动失败。
改完必须重启 MySQL 服务,且确认 SHOW VARIABLES LIKE 'character_set_server'; 返回 utf8mb4 才算生效。
已有数据乱码,不能只改配置
配置改对了,但旧表仍是 latin1 或 utf8 字符集,SELECT 出来还是乱码——因为数据本身是以错误编码存进去的。必须逐级修复:
- 改库:
ALTER DATABASE `db_name` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; - 改表(重建结构,慎用于大表):
ALTER TABLE `tb_name` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - 改列(更安全,但要重写字段定义):
ALTER TABLE `tb_name` MODIFY `content` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CONVERT TO 会锁表并重建整张表,线上环境务必避开高峰;MODIFY 只改元数据,但必须显式写出字段类型和长度,否则可能丢失约束或默认值。
真正容易被忽略的点是:即使所有配置和表结构都设成了 utf8mb4,只要客户端连接时没声明或声明错,查询结果照样乱码。字符集问题本质是链路问题,不是单点配置问题。











