必须让character_set_client、character_set_connection、character_set_results三者同时为utf8mb4,否则查询含emoji或扩展区汉字时会触发Incorrect string value错误;这是因为MySQL在返回结果前需按character_set_results转换字段值,若与实际存储的utf8mb4字节不匹配(如client为utf8),就会因解析非法四字节序列而报错。

必须让 character_set_client、character_set_connection、character_set_results 三者同时为 utf8mb4,否则查询含 emoji 或扩展区汉字时仍会报 Incorrect string value。
为什么查数据也会触发 Incorrect string value?
很多人以为这个错误只在 INSERT 时出现,其实 SELECT 同样会爆——尤其当字段值本身是乱码写入的,或客户端用 utf8 连接去读 utf8mb4 字段时,MySQL 会在返回前尝试转换,发现字节序列不合法就直接报错。典型现象是:SELECT * FROM user WHERE id = 1; 返回 Incorrect string value: '\xF0\x9F\x98\x80' for column 'name' at row 1,哪怕你只是读,没改任何东西。
根本原因不是字段存了坏数据,而是连接层字符集协商失败,导致服务端把 4 字节 UTF-8 当成 utf8(即 utf8mb3)解析,自然判为非法。
-
character_set_client:告诉 MySQL “我发给你的 SQL 里字符串是什么编码” -
character_set_connection:MySQL 内部转换用的中间编码,必须和 client 一致才能无损传递 -
character_set_results:决定返回结果(列名、字段值、警告信息)用什么编码发给客户端
如何确认当前连接的三字符集是否一致
连上 MySQL 后直接执行:
SHOW VARIABLES LIKE 'character\_set\_client';<br>SHOW VARIABLES LIKE 'character\_set\_connection';<br>SHOW VARIABLES LIKE 'character\_set\_results';
三者输出值都必须是 utf8mb4。只要有一个是 utf8 或 latin1,就存在风险。
常见陷阱:
- 用 MySQL 命令行客户端(mysql CLI)时,即使服务端配了
utf8mb4,默认也不带--default-character-set=utf8mb4,character_set_client仍是utf8 - DMS、Navicat 等 GUI 工具需手动在连接配置里勾选“UTF8MB4”或填入
charset=utf8mb4,不能依赖“自动检测” - 某些旧版 JDBC 驱动(如 mysql-connector-java 5.x)即使 URL 里写了
characterEncoding=utf8mb4,仍可能忽略,必须搭配useUnicode=true
不同客户端强制设置 utf8mb4 的实操方式
不能只靠服务端配置,每次建连都得显式声明。以下方式均需在建立连接后、执行业务 SQL 前生效:
- MySQL CLI:启动时加参数
mysql --default-character-set=utf8mb4 -u root -p;如果已连上,运行SET NAMES utf8mb4; - JDBC URL:
jdbc:mysql://host:3306/db?useUnicode=true&characterEncoding=utf8mb4,注意&是 HTML 实体,实际写配置时用& - Python PyMySQL:
pymysql.connect(..., charset='utf8mb4'),写utf8或留空都会退化 - Node.js mysql2:
{ charset: 'utf8mb4' }必须作为 connect 选项传入,环境变量MYSQL_CHARSET不起作用 - PHP PDO:
[PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"]必须带上,仅改 DSN 中的charset=utf8mb4不够
线上查询突然报错,但表和库早就是 utf8mb4 了?
大概率是某条 SQL 执行前漏了 SET NAMES utf8mb4,或者应用用了连接池,部分连接初始化时没设对,混在池里复用至今。这种问题往往在批量导数、定时任务、或新上线模块调用时集中暴露。
临时救急可加一句:
SET NAMES utf8mb4;<br>SELECT * FROM your_table WHERE ...;
但长期方案必须落到连接创建环节——比如在 Spring Boot 的 HikariCP 配置中加 connection-init-sql=SET NAMES utf8mb4,而不是依赖每个 DAO 方法开头手动 SET。
最容易被忽略的一点:即使所有配置都对,若客户端操作系统 locale 是 C 或 POSIX,某些驱动(如老版 ODBC)仍可能 fallback 到 latin1,此时必须在连接字符串里硬编码指定字符集,不能信“自动”。











