MySQL 5.7+ 默认字符集不是 utf8mb4,而是 latin1;必须在 my.cnf 的 [client]、[mysql]、[mysqld] 三段统一配置 utf8mb4,并显式指定建库建表字符集及应用连接参数,否则仍会导致乱码。

MySQL 5.7+ 启动时默认字符集不是 utf8mb4?
是的,默认不是。MySQL 5.7 默认 character_set_server 是 latin1,collation_server 是 latin1_swedish_ci,哪怕你建库时指定了 utf8mb4,新创建的数据库、表、列仍可能继承服务端默认值,导致插入中文乱码、emoji 存不进去、ORDER BY 排序异常等问题。
根本原因在于:MySQL 的字符集生效是分层的(服务器 → 数据库 → 表 → 列),且客户端连接时若未显式指定,会按服务端默认值协商编码,造成「看起来能存,实际传输过程已损坏」。
- 检查当前设置:
SHOW VARIABLES LIKE 'character\_set\_%';和SHOW VARIABLES LIKE 'collation\_%'; - 临时修改(重启失效):
SET GLOBAL character_set_server = 'utf8mb4'; SET GLOBAL collation_server = 'utf8mb4_unicode_ci'; - 真正生效必须写入配置文件并重启
mysqld
my.cnf 中该在哪几个段落写 charset 配置?
必须同时覆盖 [client]、[mysql]、[mysqld] 三个段落,缺一不可。只改 [mysqld] 只影响服务端,客户端(如 mysql 命令行、PHP PDO)仍可能用错编码握手。
[client] default-character-set = utf8mb4 [mysql] default-character-set = utf8mb4 [mysqld] character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci init_connect = 'SET NAMES utf8mb4' skip-character-set-client-handshake = FALSE
-
init_connect确保普通用户连接后自动执行SET NAMES,但对root或带SUPER权限用户无效(安全限制) -
skip-character-set-client-handshake = FALSE强制服务端以配置值响应客户端的编码协商请求(默认为 TRUE,即允许客户端“自说自话”) - 注意:MySQL 8.0+ 已废弃
init_connect对 SUPER 用户的限制,但兼容性起见仍建议保留
建库建表时还要再指定 utf8mb4 吗?
要,而且必须显式指定。即使服务端已设为 utf8mb4,新建数据库若不声明,仍可能继承旧默认值(尤其升级或迁移环境时)。
- 创建数据库:
CREATE DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - 创建数据表:
CREATE TABLE t (c VARCHAR(100)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - 已有表转换:
ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;(注意:该语句会重建表,大表慎用) - 特别注意
VARCHAR字段长度:utf8mb4 下每个字符最多占 4 字节,InnoDB 单行最大 65535 字节限制下,VARCHAR(255)实际占用字节数翻倍,极端情况下可能触发Row size too large错误
PHP/Python 连接时为什么还是乱码?
因为客户端驱动没传对编码参数,服务端配置只是基础,连接时的「协商」环节才是关键。
- PHP mysqli:
$mysqli = new mysqli($host, $user, $pass, $db); $mysqli->set_charset('utf8mb4');或在 DSN 中加;charset=utf8mb4 - PHP PDO:
new PDO($dsn, $user, $pass, [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4']); - Python PyMySQL:
conn = pymysql.connect(..., charset='utf8mb4') - Node.js mysql2:
{ charset: 'utf8mb4' }选项必须显式传入 - 错误典型表现:
、???、插入成功但 SELECT 出来是空格或问号——大概率是连接层编码没对齐










