MySQL的utf8实为utf8mb3,仅支持3字节字符,而emoji属4字节Unicode字符(如U+1F600),超出其编码范围,故插入时触发Incorrect string value错误或静默丢数据。

MySQL 的 utf8 不是真正的 UTF-8,它最多只支持 3 字节字符;而 emoji 是 4 字节 Unicode 字符(如 \xF0\x9F\x98\x8A),直接插入会触发 Incorrect string value 错误或静默丢数据。必须用 utf8mb4 才能正确存取。
为什么 utf8 插入 emoji 会报 Incorrect string value
MySQL 的 utf8 实际是 utf8mb3,只覆盖 Unicode BMP 平面(U+0000–U+FFFF)。emoji 大多落在增补平面(如 U+1F600 起),需 4 字节编码,utf8mb3 遇到这类字节序列直接拒绝:
- 报错典型信息:
Incorrect string value: '\xF0\x9F\x98\x8A' for column 'name' - 某些配置下不报错但转成
?或截断,原始数据不可恢复 - 即使表字段设为
TEXT,只要字符集仍是utf8,照样失败
utf8mb4 必须全链路生效,只改表没用
只执行 ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 是不够的。客户端连接、服务端默认值、字段定义三者必须一致,否则仍会隐式转换出乱码:
- PHP 连接时要显式声明:
mysqli_set_charset($conn, 'utf8mb4')或 PDO DSN 加;charset=utf8mb4 - MySQL 服务端需在
my.cnf中设置:character-set-server = utf8mb4和collation-server = utf8mb4_0900_ai_ci - 建表语句建议写全:
CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,避免依赖全局默认 - ThinkPHP 等框架需同步改配置项,如
DB_CHARSET => 'utf8mb4'
utf8mb4 的索引长度限制容易被忽略
InnoDB 默认单列索引最大长度为 767 字节。由于 utf8mb4 每字符按 4 字节预留空间,VARCHAR(255) 字段建索引会超限:
立即学习“PHP免费学习笔记(深入)”;
-
VARCHAR(191)是安全上限(191 × 4 = 764 字节),超过会报Specified key was too long - 若已用
VARCHAR(255)且需索引,要么改长度,要么启用innodb_large_prefix=ON(MySQL 5.7+ 默认开启)并配ROW_FORMAT=DYNAMIC - 这个限制只影响索引,不影响存储本身——
VARCHAR(255)字段仍可存满 255 个 emoji
最常漏掉的是连接层配置:表和字段都改对了,但 PHP 没调 set_charset,或者 MySQL 客户端工具(如 Navicat)仍用旧连接参数,结果还是 ????。确认是否生效,最简单是连上后执行 SELECT @@character_set_client, @@character_set_connection, @@character_set_results;,三个值都应为 utf8mb4。



















