MySQL 启动时默认字符集不生效?检查 my.cnf
中的全局配置位置
MySQL 的字符集行为由多个层级控制,但最常被忽略的是配置文件加载顺序和段落作用域。
[mysqld]段下的
character_set_server和
collation_server才真正决定新数据库的默认值;
[client]或
[mysql]段只影响客户端连接默认,不改变服务端行为。
character_set_server=utf8mb4必须放在
[mysqld]下,设成
utf8会丢数据(它实际是
utf8mb3)
collation_server=utf8mb4_unicode_ci推荐优先于
utf8mb4_general_ci(后者已弃用,且排序精度低) 修改后必须重启 MySQL 进程(
systemctl restart mysqld),仅重载配置(
mysqladmin reload)不生效
建库/建表时没指定字符集,为什么还是用了 latin1?
即使
character_set_server设为
utf8mb4,如果创建数据库时没显式声明,而 MySQL 版本低于 8.0.23 且初始化时未用
--default-character-set=utf8mb4,就可能沿用编译时默认或旧数据目录残留设置。 安全做法:建库时始终显式指定 ——
CREATE DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;已有库可修改:执行
ALTER DATABASE db_name CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;(注意这不改变已有表字段的字符集) 检查当前库实际编码:
SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = 'db_name';
连接后执行 SET NAMES utf8mb4
还是乱码?确认客户端协议层是否对齐
SET NAMES utf8mb4只设置当前连接的
character_set_client、
character_set_results、
character_set_connection,但如果应用代码里连接字符串没传参,或驱动自动协商失败,仍可能回退到
latin1。 PHP PDO 示例:DSN 中必须加
;charset=utf8mb4,光靠
SET NAMES不保险 Java JDBC:URL 加
?characterEncoding=utf8mb4&useUnicode=true,否则 Connector/J 8.0+ 默认用
utf8mb3验证连接实际编码:
SHOW VARIABLES LIKE 'character_set%';看三者是否均为
utf8mb4
字段级字符集比库级还关键:ALTER TABLE ... CONVERT TO
的副作用
用
CONVERT TO升级表字符集看似方便,但它会强制重写所有字段为
utf8mb4,可能意外截断超长索引(如
VARCHAR(255)在
utf8mb4下索引长度翻倍),甚至让原本能存 4 字节 emoji 的字段因隐式转换失败而报错。 更稳妥方式:逐字段修改 ——
ALTER TABLE t MODIFY c VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;先检查字段当前定义:
SHOW CREATE TABLE t;,留意
CHARACTER SET是否为空(继承库级)还是已显式指定 有全文索引或生成列的表,
CONVERT TO可能失败,需先删索引再重建
字符集问题从来不是改几个配置就能一劳永逸的事——连接、服务端、库、表、字段、索引、客户端驱动,六层嵌套,漏一层就可能在某个边界场景突然崩掉。特别是 emoji 和中文混合搜索时的排序行为,
utf8mb4_unicode_ci和
utf8mb4_0900_as_cs的差异,往往要到上线后查不出数据才暴露。
