MySQL 5.7及更早版本必须显式设置PHP连接层字符集为utf8mb4,因phpMyAdmin不自动发送SET NAMES utf8mb4,且默认连接字符集为utf8mb3;MySQL 8.0虽默认utf8mb4,但phpMyAdmin旧版本可能不兼容utf8mb4_0900_ai_ci,推荐统一用utf8mb4_unicode_ci,并验证character_set_client、connection、results三者全为utf8mb4。
MySQL 5.7 及更早版本:必须显式设置连接层
phpmyadmin 本身不自动发 set names utf8mb4,而 mysql 5.7 默认连接字符集仍是 utf8(即 utf8mb3),即使数据库和表都设为 utf8mb4,插入 emoji 也会报 incorrect string value。你得在 php 连接层面补上这一环:
- 如果用 PDO,构造时必须加
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4" - 如果用 mysqli,连接后立即调用
$mysqli->set_charset('utf8mb4') - phpMyAdmin 的
config.inc.php中,可强制全局连接字符集:$cfg['Servers'][$i]['connection_charset'] = 'utf8mb4';
漏掉这一步,SHOW VARIABLES LIKE 'character_set%' 里 character_set_client 和 character_set_connection 仍会是 utf8,链路就断了。
MySQL 8.0+:默认行为变了,但仍有陷阱
MySQL 8.0 开始,默认 character_set_server 和 collation_server 已是 utf8mb4 和 utf8mb4_0900_ai_ci,但 phpMyAdmin(尤其旧版本)可能不识别新排序规则。常见问题:
-
utf8mb4_0900_ai_ci在 phpMyAdmin 表结构页下拉菜单里可能不显示,选了也无效 - 建库语句若写
COLLATE utf8mb4_0900_ai_ci,部分 phpMyAdmin 版本会报语法错误 - 推荐退半步:统一用
utf8mb4_unicode_ci,兼容性更好,排序逻辑也足够通用
验证时别只看 @@character_set_database,还要查 SELECT @@character_set_client, @@character_set_connection, @@character_set_results —— 三者必须全是 utf8mb4 才算真正通。
WampServer / XAMPP 环境:my.ini 修改位置容易错
Windows 下 WampServer 或 XAMPP 的 my.ini 有多个 section,只改 [mysqld] 不够:
立即学习“PHP免费学习笔记(深入)”;
- 必须同时在
[client]和[mysql]段落加default-character-set=utf8mb4 - 否则命令行工具(如
mysql -u root)连上去仍是utf8,导致你在 phpMyAdmin 里看到的和终端里执行的不一致 - 重启服务后,运行
SHOW VARIABLES LIKE 'character_set%',确认character_set_filesystem以外的所有character_set_*都是utf8mb4
WampServer 自带的 MySQL 5.5/5.7 默认配置文件常被忽略,my.ini 可能在 C:\wamp64\bin\mysql\mysqlX.X.X\my.ini,而不是系统目录。
字段级修改:ALTER TABLE CONVERT TO 并非万能
对已有表执行 ALTER TABLE tbl_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci 是常用操作,但它只改字段定义,不处理已存数据的实际编码:
- 如果原数据是用
latin1存的中文,转完还是乱码;必须先确保原始字节是 UTF-8 编码 - 含 TEXT/BLOB 字段时,
CONVERT TO可能触发隐式复制,大表要卡住几秒甚至几分钟 - 更安全的做法是分两步:
ALTER TABLE tbl_name MODIFY col_name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;,逐列改,可控性强
最易被跳过的是数据迁移前的编码校验 —— 先用 SELECT HEX(col_name) FROM tbl_name LIMIT 1 看十六进制值,确认不是 EFBFBD(UTF-8 替换符),再动手转换。



















