必须统一客户端、连接层、服务端、数据库、表、字段六层字符集为utf8mb4;需用show variables like 'character_set%'逐层验证character_set_client、connection、results、database、server是否全为utf8mb4,配置须同时修改[mysqld]和[client]段并重启,应用连接及字段定义也必须显式声明utf8mb4。

必须统一客户端、连接层、服务端、数据库、表、字段六层字符集为 utf8mb4,缺一不可;只改配置或只改表结构,90% 会继续乱码。
查清哪一层断了:用 SHOW VARIABLES LIKE 'character_set%' 看实时值
执行这条命令后,重点盯住这五个变量是否全为 utf8mb4:
-
character_set_client:客户端发来的 SQL 按什么解码(不是你终端显示的编码) -
character_set_connection:MySQL 内部转换用的“中转站”,必须和 client 一致 -
character_set_results:返回结果时声明用什么编码,客户端必须按此读取 -
character_set_database:当前库默认字符集,影响新建表 -
character_set_server:服务端兜底默认值,新建库若没显式指定就继承它
常见错误现象:前三个全是 latin1,但 character_set_server 是 utf8mb4 —— 这说明连接协商失败,数据在进库前就被截断了。别信“服务器设对了就行”。
配置文件必须同时改 [mysqld] 和 [client] 段
只在 [mysqld] 下写 character-set-server = utf8mb4 是不够的——新连接仍可能从 [client] 段读不到默认值,退回到 latin1。正确写法是:
[mysqld] character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci [client] default-character-set = utf8mb4 [mysql] default-character-set = utf8mb4
注意:utf8 是 MySQL 的历史陷阱,最多支持 3 字节,存不了 emoji 和部分生僻字;utf8mb4 才是真 UTF-8。重启服务后必须再运行 SHOW VARIABLES 验证,不验证等于没改。
ALTER TABLE ... CONVERT TO CHARACTER SET 不等于修好了
执行 ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci 只解决表结构层面,但有三个坑:
- 如果字段定义里显式写了
CHARACTER SET latin1(比如建表时手写的),CONVERT TO不会覆盖它,字段仍用 latin1 存储 - 已有乱码数据(比如本该是 UTF-8 字节却被当 latin1 存进去了)不会自动修复,转换后还是乱码
- 大数据量表执行时会锁表,且重建过程不可中断;建议先用
SHOW CREATE TABLE t确认字段是否真带utf8mb4声明
更稳妥的做法是单独改字段:ALTER TABLE t MODIFY COLUMN content TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,可控性更强。
应用连接时必须显式传 charset=utf8mb4
即使服务端和表都对了,应用连接不声明字符集,MySQL 仍会按协议默认协商出 latin1。不同驱动写法不同:
- Python
pymysql:charset='utf8mb4'参数必须传,不能只靠配置文件 - Java JDBC:
?useUnicode=true&characterEncoding=utf8mb4要写在 URL 末尾 - PHP PDO:
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"推荐,比只设 DSN 更可靠 - 命令行:
mysql -u root -p --default-character-set=utf8mb4 db_name启动,进去后立刻SET NAMES utf8mb4
漏掉这一环,前面所有配置都白搭——因为乱码发生在数据“进”和“出”的瞬间,不在存储时。
最易被忽略的是字段级显式声明和应用连接参数这两处;很多人改完配置、跑完 CONVERT TO 就以为结束了,结果插入微信昵称或 emoji 时又报 Incorrect string value,其实只是某一个字段还卡在 utf8 或连接根本没生效。











