mysql的utf8实为utf8mb3,不支持4字节emoji等字符,需改用utf8mb4字符集并配对客户端、html及http编码。

为什么插入emoji会报 Incorrect string value
这是 MySQL utf8 字符集最典型的“翻车现场”——它根本不是真正的 UTF-8,只支持最多 3 字节的 Unicode 字符(U+0000–U+FFFF),而 emoji(如 ?、?)、部分生僻汉字(如 ?)、数学符号(如 ?)都是 4 字节字符,落在 U+10000–U+10FFFF 范围内,utf8 直接拒绝存储。
- 错误示例:
INSERT INTO users(name) VALUES('小明?');→ 报错Incorrect string value: '\xF0\x9F\x98\x8A'... - 根本原因:MySQL 的
utf8实际是utf8mb3,是历史命名错误,官方已明确承认 - 解决方案不是调客户端编码,而是把底层字符集换成
utf8mb4——它是唯一能存下这些字符的 MySQL 原生字符集
建表和改表时必须显式指定 utf8mb4,不能依赖服务器默认值
即使你用 SHOW VARIABLES LIKE 'character_set_server'; 看到返回 utf8mb4,也不代表已有表或字段就自动生效。MySQL 的字符集继承是“逐层覆盖”的:服务端 → 数据库 → 表 → 列,任意一层没设对,就会回退到旧配置。
- 新建表务必写全:
CREATE TABLE comments (id INT, content TEXT) CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - 修改已有表:
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;(推荐)或更精细地改列:ALTER TABLE users MODIFY name VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; - 注意:如果字段有索引,且原
VARCHAR(255)在utf8下刚好卡在 767 字节限制边缘,改成utf8mb4后可能超限(4×255=1020 > 767),需同步缩短长度或开启innodb_large_prefix
utf8mb4_0900_ai_ci 是 MySQL 8.0+ 默认排序规则,比 utf8mb4_unicode_ci 更准
字符集决定“能存什么”,排序规则(collation)决定“怎么比大小、是否区分大小写、是否忽略重音”。老项目常用 utf8mb4_unicode_ci,但它基于较旧的 Unicode 标准,在德语 ä、西班牙语 ñ 或带变音符号的越南文上排序可能不准。
-
utf8mb4_0900_ai_ci是 MySQL 8.0 引入的,默认启用 Unicode 9.0.0 标准,ai=accent insensitive,ci=case insensitive,对多语言排序更鲁棒 - 若需大小写敏感,选
utf8mb4_0900_as_cs;若要兼容旧应用行为,可保留utf8mb4_unicode_ci,但新项目建议直接用_0900_ai_ci - 验证当前字段 collation:
SHOW FULL COLUMNS FROM users LIKE 'name';→ 看Collation列是否为utf8mb4_0900_ai_ci
客户端连接也得配对,否则前功尽弃
就算表和字段全设成 utf8mb4,如果客户端连上来用的是 utf8,MySQL 仍会按 3 字节逻辑解析请求,导致乱码或截断。关键参数有三个:character_set_client、character_set_connection、character_set_results。
- 连接时显式指定(以 MySQL CLI 为例):
mysql --default-character-set=utf8mb4 -u root -p - 应用层(如 Python PyMySQL)需在连接参数中加:
charset='utf8mb4';PHP PDO 加PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4" - 检查是否生效:
SHOW VARIABLES LIKE 'character_set%';→ 确保client、connection、results三者均为utf8mb4
最容易被忽略的是:哪怕数据库、表、列、连接全设对了,如果前端 HTML 没声明 <meta charset="UTF-8">,或者 HTTP 响应头漏了 Content-Type: text/html; charset=utf-8,用户看到的还是方块或问号——字符集是一条链,断哪一环都不行。










