MySQL 5.7+ 中需全链路升级 utf8mb4 才支持 Emoji,因 utf8 最多存 3 字节,而 Emoji 需 4 字节;须统一配置 character-set-server、collation-server 和 init_connect,并重启验证;ALTER TABLE 转换时注意索引长度超限,可改用 VARCHAR(191)。
MySQL 5.7+ 中 utf8 不支持 Emoji,必须切到 utf8mb4
mysql 的 utf8 是个历史包袱——它最多只存 3 字节字符,而 emoji(如 ?、??)需要 4 字节,直接存会截断或报错 incorrect string value。这不是配置没生效,是底层编码能力根本不够。必须全链路升级到 utf8mb4,包括服务端、数据库、表、列、连接,缺一不可。
修改 my.cnf 后重启不生效?检查这三处配置是否一致
很多人改了 [mysqld] 段的 character-set-server=utf8mb4 就以为完事,结果建新表还是 utf8。关键在三个地方必须统一:
-
character-set-server=utf8mb4(服务级默认) -
collation-server=utf8mb4_unicode_ci(排序规则,推荐utf8mb4_unicode_ci或utf8mb4_0900_as_cs) -
init_connect='SET NAMES utf8mb4'(确保普通用户连接时自动设置,但注意:对root或有SUPER权限的用户无效)
改完必须重启 MySQL,用 SHOW VARIABLES LIKE 'character_set%'; 和 SHOW VARIABLES LIKE 'collation%'; 双重确认。
ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4 的坑:索引长度超限
执行转换语句后可能报错 Specified key was too long,尤其在 VARCHAR(255) 加了索引的字段上。因为 utf8mb4 下单字符最多占 4 字节,255×4 = 1020 字节,超过 InnoDB 单索引键 767 字节(旧版本)或 3072 字节(5.7.7+ 配合 innodb_large_prefix=ON)限制。
- 临时解法:缩短字段长度,比如
VARCHAR(191)(191×4 = 764 - 根治法:确认
innodb_file_format=Barracuda、innodb_file_per_table=ON、innodb_large_prefix=ON,再用ROW_FORMAT=DYNAMIC重建表 - 别漏掉
TEXT类型字段——它们不会因转换自动变utf8mb4,需显式指定MODIFY COLUMN
应用层连接参数不配 charset=utf8mb4,前面全白干
即使数据库全改成 utf8mb4,如果应用连接时没声明字符集,MySQL 仍按默认(可能是 latin1 或旧 utf8)解析请求,Emoji 传进去就乱码或丢弃。
- PHP PDO:DSN 加
;charset=utf8mb4,例如mysql:host=localhost;dbname=test;charset=utf8mb4 - Python PyMySQL:初始化时传
charset='utf8mb4' - Java JDBC URL:加
?characterEncoding=utf8mb4&useUnicode=true - Node.js mysql2:配置项设
charset: 'utf8mb4'
最易忽略的是 ORM 默认行为——Django 的 OPTIONS={'charset': 'utf8mb4'}、Laravel 的 charset 和 collation 都要显式写,不能依赖数据库全局设置。
真正麻烦的不是改配置,而是验证每个环节:服务端变量、库/表/列的 CHARACTER SET 和 COLLATION、连接时的 character_set_client/character_set_connection/character_set_results,三者必须全是 utf8mb4。少一个,Emoji 就可能悄无声息地变问号或报错。










