问题是什么样子
修复前
INSERT INTO wp_posts (post_title) VALUES ('䏿–‡ 北京 上海');修复后
INSERT INTO wp_posts (post_title) VALUES ('中文 北京 上海');原因在哪里
WordPress 以 UTF-8 保存文字。但很多老站的数据表是用 latin1 建的,或者用 latin1 连接数据库。只要写入和读取犯的是同一个错,平时没人察觉。换到新服务器、用 phpMyAdmin 导出,或改了连接字符集之后,所有中文就会同时变成乱码。
wp-config.php里的DB_CHARSET是latin1,或与数据表不一致。- 备份用
latin1导出,却用utf8mb4导入,或者反过来。 - 某个插件或导入脚本把已经乱掉的文字又转了一次,这就是二次乱码。
修复导出文件
- 改动任何东西之前,先备份数据库。
- 把有问题的表导出为
.sql文件,可以用 phpMyAdmin,也可以用mysqldump --default-character-set=utf8mb4。 - 把文件拖进上面的输入框。Mojibuster 只修复坏掉的部分,并列出每一处改动。
- 下载修复后的文件,用
utf8mb4导入一个全新的测试数据库。 - 检查网站显示无误后,再导入正式数据库。
备份文件不会被上传,这一点很重要,因为里面通常有用户账号、邮箱地址和评论。
小心序列化数据
WordPress 会把部分设置以 PHP 序列化的形式存进 wp_options 和 wp_postmeta,例如 s:6:"中文";。其中的数字是字节数,不是字数。把乱码还原成中文后长度就对不上了,WordPress 会直接丢弃这个值。所以请先修复 wp_posts、wp_comments、wp_terms 这类内容表;选项类数据更适合用 WP-CLI 的 wp search-replace,它会同步更新长度。
在 MySQL 中解决根本原因
- 把数据表和连接都改为
utf8mb4,并在wp-config.php中设置define('DB_CHARSET', 'utf8mb4');。 - 如果
latin1列里存的其实是 UTF-8 字节,不要直接转换,否则会被再编码一次。常规做法是经过二进制类型中转:先MODIFY … LONGBLOB,再MODIFY … LONGTEXT CHARACTER SET utf8mb4。 - 所有改动都先在数据库副本上试验。
常见问题
能一次修复整个 WordPress 数据库吗?
可以,较大的 SQL 备份也能处理。但要留意 wp_options 和 wp_postmeta 中的序列化数据,这类数据用 WP-CLI 更合适。
备份里本来正确的字符会被改动吗?
不会。Mojibuster 只修复明显损坏的字符序列,正确的文字保持原样。
utf8 和 utf8mb4 有什么区别?
MySQL 的 utf8 最多只存 3 字节的字符,表情符号会丢失。utf8mb4 才是完整的 UTF-8,WordPress 应该用它。
除了 WordPress,其他系统也适用吗?
适用。Joomla、Drupal、Magento,或基于 MySQL、MariaDB 的自研系统都有同样的问题,解决办法也一样。