MySQL 中文网站出现乱码,通常不是单纯因为数据库“没有设置 UTF-8”,而是数据存储、数据库默认字符集、表和列定义、客户端连接以及网页输出之间存在不一致。真正稳定的做法,是把这些环节分别确认,而不是看到乱码后盲目执行一条转换命令。
字符集(Character Set)决定数据能够使用什么编码方式保存和传输;校对规则(Collation)则决定字符串如何比较、排序以及是否区分大小写、重音等。MySQL 对字符集和校对规则采用多层默认机制,包括服务器、数据库、表和列四个层级,同时还有一个经常被忽略的连接层。
对于现代中文网站,通常应优先考虑 utf8mb4。MySQL 所谓的 utf8 实际上对应旧的三字节 UTF-8 实现,不能完整覆盖 Unicode 的四字节字符;utf8mb4 才能够覆盖完整的 Unicode 范围。因此,包含中文、Emoji、部分特殊符号以及其他扩展字符的网站,没有继续使用 MySQL utf8 的必要。MySQL 从5.5系列开始提供 utf8mb4 支持。
如果希望服务器默认使用 utf8mb4,可以在 MySQL 配置文件中设置:
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_0900_ai_ci
不过,这个校对规则并不是所有 MySQL 版本都存在。utf8mb4_0900_ai_ci 是 MySQL 8.0 引入的新一代 Unicode 校对规则,MySQL 8.0 默认服务器字符集就是 utf8mb4,默认校对规则为 utf8mb4_0900_ai_ci。如果运行的是较老版本,就应该根据该版本实际支持的校对规则进行选择。
创建数据库时,也可以明确指定字符集和校对规则:
CREATE DATABASE mydb
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
数据库级设置主要是默认值。以后在这个数据库中创建表,如果没有另外指定字符集,相关字符列通常会继承数据库默认设置。因此,数据库设置正确以后,新表一般不需要每个字段都重复写一遍字符集。
创建表时可以进一步明确指定:
CREATE TABLE mytable (
id INT PRIMARY KEY,
content TEXT
) DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
如果某个字段需要特殊的字符集或校对规则,也可以在列级指定:
content TEXT
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci
列级定义的优先级高于表级默认值。因此,实际项目中没有必要为了“保险”而给每一个字段都重复指定 utf8mb4。只要数据库、表以及特殊字段的定义清楚即可。
校对规则不能简单理解成“支持中文”和“不支持中文”。例如 utf8mb4_bin 同样可以保存和排序中文,只是比较方式更接近按照字符编码值进行比较;它与 Unicode 语言校对规则得到的排序结果可能不同。因此,“utf8mb4_bin无法进行中文排序”是不准确的。
如果网站主要面向中文内容,选择哪一种 collation,应根据实际搜索、排序和大小写敏感需求决定。utf8mb4_unicode_ci 是较常见的旧版 Unicode 校对规则,而 MySQL 8.0 系列还提供 utf8mb4_0900_ai_ci 以及其他更具体的 Unicode 校对规则。不要把某一个 collation 当成所有网站的唯一标准答案。
数据库设置正确以后,连接字符集仍然非常重要。MySQL 为每一个客户端连接维护 character_set_client、character_set_connection 和 character_set_results 等会话变量。客户端发送 SQL、服务器解释 SQL 以及服务器返回结果时,都涉及连接字符集。
PHP PDO 最简单的做法,是直接在 DSN 中指定:
$dsn = 'mysql:host=localhost;dbname=mydb;charset=utf8mb4';
$pdo = new PDO(
$dsn,
$username,
$password,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]
);
PDO 官方文档明确提供了 DSN 的 charset 参数,因此如果已经在连接字符串中指定 charset=utf8mb4,通常不需要再额外执行一遍 SET NAMES utf8mb4。
如果使用 MySQLi,则建议直接使用:
mysqli_set_charset($conn, 'utf8mb4');
这比单纯执行:
$conn->query("SET NAMES utf8mb4");
更适合作为 PHP MySQLi 连接初始化方式。PHP 官方文档特别指出,mysqli_set_charset() 会影响连接使用的字符集,而单纯通过 SQL 执行 SET NAMES 并不会改变 mysqli_real_escape_string() 所使用的字符集。
命令行工具也存在连接字符集问题。例如使用 mysqldump 时,可以明确指定:
mysqldump -u user -p --default-character-set=utf8mb4 mydb > dump.sql
恢复时同样要注意导入端的字符集配置。否则即使数据库和表本身全部采用 utf8mb4,数据在导出、传输或者导入过程中仍然可能出现编码转换问题。
排查乱码时,不要只检查 character_set_server。首先查看服务器和当前连接:
SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';
如果只想检查当前连接,还可以查看:
SHOW SESSION VARIABLES LIKE 'character_set_%';
SHOW SESSION VARIABLES LIKE 'collation_%';
MySQL 官方建议通过连接会话变量检查客户端和服务器之间实际使用的字符集。
然后检查数据库:
SELECT
SCHEMA_NAME,
DEFAULT_CHARACTER_SET_NAME,
DEFAULT_COLLATION_NAME
FROM information_schema.SCHEMATA
WHERE SCHEMA_NAME = 'mydb';
检查表结构:
SHOW CREATE TABLE mytable;
检查具体字段:
SELECT
COLUMN_NAME,
CHARACTER_SET_NAME,
COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'mydb'
AND TABLE_NAME = 'mytable';
这一步比单纯检查服务器默认字符集更重要,因为一个已经存在的旧表完全可能继续使用 latin1 或 MySQL utf8mb3,即使服务器现在已经改成 utf8mb4。
对于新建网站,建议从数据库、表到应用连接统一规划。例如:
CREATE DATABASE mydb
CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
然后:
CREATE TABLE articles (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
content LONGTEXT NOT NULL,
PRIMARY KEY (id)
) CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
PHP连接:
$pdo = new PDO(
'mysql:host=localhost;dbname=mydb;charset=utf8mb4',
$username,
$password
);
网页输出则明确声明:
header('Content-Type: text/html; charset=UTF-8');
这里需要特别区分两个概念:MySQL 中的字符集名称通常写成 utf8mb4,HTTP 和 HTML 中则通常写成 UTF-8。它们属于不同层面的表示方式,并不矛盾。
如果旧网站已经存在乱码,最不能做的事情就是看到乱码以后立即执行:
ALTER TABLE mytable
CONVERT TO CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
这个语句适合在原有数据编码正确、只是列或表字符集需要转换的情况下使用。它并不是“乱码修复按钮”。MySQL 官方明确说明,字符集转换要求非二进制字符串列中的内容本身应该按照原字符集正确编码。如果数据库中已经混入错误解释后的数据,直接转换可能无法恢复原始文字。
例如数据库中原本应该是“中文”,却已经显示成 䏿–‡,首先应该判断这是 UTF-8 字节被错误地按照另一种字符集解释,还是数据在写入时已经真正损坏。两种情况的处理方法完全不同。
因此,修复旧数据库前应该先备份数据库,再抽取少量问题数据进行测试,确认原始字节、字段字符集、连接字符集以及导入导出过程以后,再决定是否执行 ALTER TABLE ... CONVERT TO CHARACTER SET。字符集转换涉及表结构和数据重建,实际操作还需要考虑索引长度、表规模、锁和业务停机窗口。MySQL 文档也指出,字符集转换可能导致表重建。
还有一个常见误区,是认为“只要数据库使用 utf8mb4,中文网站就不会乱码”。实际上,一个完整的网站至少存在浏览器、HTML/HTTP、PHP字符串、数据库连接、MySQL字段、导入导出工具等多个编码边界。任何一个边界出现不一致,都可能产生乱码。
因此,真正可靠的配置原则不是简单地把所有地方机械地改成一个字符串,而是确认每一个数据传递环节使用了正确且一致的字符编码。对于现代 MySQL 中文网站,utf8mb4 通常是合理的基础选择;校对规则根据 MySQL 版本和业务排序需求选择;应用连接明确指定字符集;旧数据转换前先确认实际编码状态。
只要把“存储字符集”“连接字符集”“校对规则”和“网页输出编码”分开检查,MySQL 中文乱码问题就不再是一个只能靠反复试错解决的问题,而会变成一个可以沿着数据链路逐段定位的工程问题。
MySQL 字符集怎么设置?中文网站避免乱码必须掌握的基础知识
图片说明:示意图 图片来源:Public Domain(公有领域)
喜欢这篇报道?
使用下面的功能,方便以后继续阅读和分享 MNewsTV
关于文章收藏
收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。
删除收藏请进入「我的收藏」进行管理。
捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP