mysql 8.0 默认用 utf8mb4 是因旧 utf8(即 utf8mb3)仅支持 3 字节 unicode(u+0000–u+ffff),无法存储 emoji、生僻汉字等 4 字节字符,插入会报 incorrect string value;utf8mb4 才是完整 utf-8 实现,自 5.5.3 存在,8.0 仅设为默认。

MySQL 8.0 默认用 utf8mb4,不是为了“更现代”,而是因为旧的 utf8(实际是 utf8mb3)根本存不了 emoji、生僻汉字、越南语多音符字——一插就报 Incorrect string value。
为什么 MySQL 的 utf8 不能存 emoji
MySQL 的 utf8 是个历史包袱:它只实现 Unicode 3.0 的子集,最多支持 3 字节编码(U+0000–U+FFFF),而 emoji 如 ?(U+1F60A)、汉字“?”、数学符号“?”都落在 4 字节区(U+10000–U+10FFFF)。服务端看到这类字节序列(比如 \xF0\x9F\x98\x8A),直接拒绝写入。
真正完整的 UTF-8 实现是 utf8mb4,“mb4” 就是 “max bytes 4” 的意思。它从 MySQL 5.5.3 就已存在,8.0 只是把它设为默认——补上本该有的基础能力。
-
utf8mb3和utf8在 MySQL 中完全等价,都是伪 UTF-8 - 所有 4 字节字符插入失败时,错误信息里一定含
Incorrect string value和十六进制字节串 - 即使客户端用
SET NAMES utf8,只要字段定义仍是CHARSET=utf8,照样失败
MySQL 8.0 升级后为什么老表还是不支持 emoji
8.0 启动后执行 SHOW VARIABLES LIKE 'character_set_server' 返回 utf8mb4,不代表万事大吉。已有库、表、字段的字符集不会自动改,只影响新建对象。
查一个表的真实编码,必须用:SHOW CREATE TABLE table_name,看输出里 DEFAULT CHARSET=... 和各字段的 CHARACTER SET 是否为 utf8mb4。
- 升级前建的表,即使 server 默认是
utf8mb4,字段仍可能是latin1或utf8 -
init_connect='SET NAMES utf8'在 8.0 下会被静默映射成utf8mb4,但仅作用于连接层,不改变字段定义 - 如果字段是
utf8,哪怕连接和 server 都是utf8mb4,插入四字节字符仍失败
如何验证当前环境是否真正支持 emoji
不能只看变量,得实测。最简验证方式:
INSERT INTO t1 (name) VALUES ('?');
如果成功,再查:SELECT HEX(name), name FROM t1;,确认返回的是 F09F988A(4 字节)而非截断或问号。
- 测试前确保:连接参数(如 PHP 的
charset=utf8mb4)、表定义、字段定义三者全为utf8mb4 - 若用宝塔面板,
[client]段也需加default-character-set = utf8mb4,否则命令行客户端仍可能用错编码 - 注意排序规则:推荐
utf8mb4_0900_ai_ci(8.0+ 默认),避免用已废弃的utf8mb4_general_ci
ALTER TABLE 转换 utf8 到 utf8mb4 的关键点
执行 ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci 看似简单,但有隐性依赖:
- 必须先开启
innodb_large_prefix = ON,否则VARCHAR(255)类字段加索引会因长度超 767 字节失败 - 表引擎必须是
InnoDB,且文件格式为Barracuda(innodb_file_format = Barracuda) - 转换后要检查索引是否完整:
SHOW INDEX FROM t,部分索引可能被自动删掉 - 不要只改表,还要显式改字段:
MODIFY COLUMN c VARCHAR(100) CHARACTER SET utf8mb4,防止某些字段残留旧定义
真正麻烦的从来不是改配置,而是确认每个环节——连接、server、database、table、column、甚至客户端工具本身的编码设置——全都对齐 utf8mb4。漏掉任意一层,emoji 就会变成乱码或报错。











