本文约 3,560 字,预计阅读 9 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:内容里有个 emoji,文章就存不进去
典型症状:文章里带表情符号,保存时提示失败或直接截断;或者从别的系统导入数据后,中文变成 ??? 或 å 这类乱码。
第一个问题的根因是字符集:MySQL 的 utf8 只支持 3 字节,而 emoji 和部分汉字扩展区字符需要 4 字节,必须用 utf8mb4。第二个问题则多半是连接字符集与存储字符集不一致。
原理分析:三个字符集概念
| 概念 | 说明 |
|---|---|
| 字符集(charset) | utf8(3 字节)、utf8mb4(4 字节)、latin1 |
| 排序规则(collation) | 决定字符串比较与排序,如 utf8mb4_unicode_ci、utf8mb4_general_ci、utf8mb4_0900_ai_ci |
| 连接字符集 | 客户端与服务端通信用的编码,由 wp-config.php 的 DB_CHARSET 决定 |
WordPress 默认的 DB_CHARSET 是 utf8mb4(自 4.2 起),DB_COLLATE 通常留空(跟随数据库默认)。
排序规则的选择:
utf8mb4_unicode_ci:按 Unicode 标准排序,准确性最好,稍慢;utf8mb4_general_ci:快但排序规则简单(部分语言排序不准);utf8mb4_0900_ai_ci:MySQL 8 默认,基于 UCA 9.0.0,MariaDB 不认识。
跨引擎迁移时统一用 utf8mb4_unicode_ci 最省心。
保姆级实操
第一步:检查当前状态
-- 库的字符集
SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
FROM information_schema.SCHEMATA WHERE SCHEMA_NAME='wordpress';
-- 各表的字符集
SELECT TABLE_NAME, TABLE_COLLATION
FROM information_schema.TABLES WHERE TABLE_SCHEMA='wordpress';
-- 各字段的字符集
SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA='wordpress' AND DATA_TYPE IN ('varchar','text','longtext','char');
用 WP-CLI:
wp db query "SELECT @@character_set_database, @@collation_database;"
wp config get DB_CHARSET
第二步:备份
mysqldump -u root -p --default-character-set=utf8mb4 --single-transaction wordpress > before-charset.sql
第三步:确认索引长度问题(关键)
utf8mb4 每个字符占 4 字节,InnoDB 单列索引上限 767 字节(老版本 innodb_large_prefix 关闭时)。如果你有 varchar(255) 且建了索引,转换后可能超限报错:
Specified key was too long; max key length is 767 bytes
解法一(MySQL 5.7.7+ 默认支持 3072 字节):确认参数
innodb_large_prefix = ON
innodb_file_format = Barracuda
innodb_file_per_table = ON
解法二:把字段缩短,如 varchar(255) 改 varchar(191)。
第四步:转换数据库
ALTER DATABASE wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
这只影响新建表的默认值,已有表需要单独转。
第五步:逐表转换(生成语句)
SELECT CONCAT('ALTER TABLE `', TABLE_NAME, '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;')
FROM information_schema.TABLES
WHERE TABLE_SCHEMA='wordpress' AND TABLE_TYPE='BASE TABLE';
把输出的语句批量执行。CONVERT TO 会同时转换表内所有字符字段,比逐个字段改方便。
WP-CLI 方式:
wp db query "ALTER TABLE wp_posts CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
或者用插件/脚本批量:
for t in $(wp db query "SHOW TABLES" --skip-column-names); do
wp db query "ALTER TABLE $t CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
done
第六步:确认 wp-config.php
define('DB_CHARSET', 'utf8mb4');
define('DB_COLLATE', '');
DB_COLLATE 留空表示跟随数据库默认。若明确指定,必须与数据库实际排序规则一致,否则可能出现 Illegal mix of collations。
第七步:修复”双重编码”乱码
如果转换后中文仍显示为 å 之类,说明是双重编码(原本 utf8 的数据被当成 latin1 存了一遍)。修复方式:
-- 先转回 latin1 的二进制视角,再转 utf8mb4
ALTER TABLE wp_posts MODIFY post_content LONGTEXT CHARACTER SET latin1;
ALTER TABLE wp_posts MODIFY post_content LONGBLOB;
ALTER TABLE wp_posts MODIFY post_content LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
操作前必须备份,且先在单表上试。
第八步:验证
UPDATE wp_posts SET post_title='测试 emoji ????' WHERE ID=1;
SELECT post_title, HEX(post_title) FROM wp_posts WHERE ID=1;
能正常存读 emoji 即成功。
常见踩坑与失败案例
- 只改了库没改表 —— 新建表是新字符集,老表还是 utf8,混合状态最麻烦。
Illegal mix of collations—— 表之间排序规则不一致,JOIN 时报错。统一成一种。- 案例:某站从 MySQL 5.6(utf8)迁到 MySQL 8(utf8mb4_0900_ai_ci)后,一个多语言插件的排序报错。最后把所有表统一成
utf8mb4_unicode_ci解决。 - 索引超长导致转换失败 —— 大表转换耗时长且可能中断。建议在低峰期操作,先用小表验证流程。
- 改了 DB_CHARSET 但没转数据 —— 连接用 utf8mb4 而存储是 utf8,写入时反而更容易出错。
总结
字符集问题的处理顺序是:先备份 → 检查现状 → 确认索引长度 → 转库 → 逐表 CONVERT TO → 核对 wp-config.php → 验证 emoji。排序规则我建议统一用 utf8mb4_unicode_ci,它在 MySQL 与 MariaDB 之间都能用,避免跨引擎迁移的坑。至于已经出现的双重编码乱码,要在备份基础上用”latin1 → blob → utf8mb4″三步法修复,且务必先拿单表试,确认效果再全量执行。
