编程 MySQL 报 ER_1366 Incorrect string value:从驱动到字段排查连接字符集链

2026-10-16 00:05:00

MySQL 报 ER_1366 Incorrect string value:从驱动到字段排查连接字符集链

参考:MySQL 8.4 手册 12.4 Connection Character Sets and Collations

报错原文

Warning: #1300 Invalid utf8 character string: 'F09F8D'
Warning: #1366 Incorrect string value: '\xF0\x9F\x8D\xB0' for column 'Text' at row 1
java.sql.SQLException: Incorrect string value: '\xF0\x9F\x90\xB0\xE5\xA4...' for column 'username' at row 1 ; error code [1366]
General error: 3988 Conversion from collation utf8mb3_general_ci into utf8mb4_general_ci impossible for parameter

\xF0\x9F\x8D\xB0 就是 🍰 的 UTF-8 字节。

根因:utf8 是 utf8mb3 的别名

MySQL 里的 utf8 等于 utf8mb3,每字符最多 3 字节,编码上限是 BMP(0xFFFF)。emoji(🍰 🐰 🎶)是 4 字节字符,一部分生僻汉字和数学符号同样是 4 字节。MySQL 5.5.3 起才有 utf8mb4(most bytes 4)。

写入失败的路径大致是这几条:

  • 4 字节字符写进 utf8 列 —— 列本身要改成 utf8mb4
  • 列已经是 utf8mb4 仍报错 —— 连接字符集和列字符集不匹配,客户端发来的字节服务器解释不了
  • 迁移时未转换的二进制数据(latin1 → utf8 没动字节)
  • 应用发原始字节,驱动的默认字符集和数据不一致
  • LOAD DATA INFILE 用了错误的 CHARACTER SET 子句

客户端 / 连接层 / 服务端对象三处都要 utf8mb4

  • 客户端:保证输出的字符串是 utf8mb4
  • 连接层:连接字符集是 utf8mb4
  • 服务端对象:实例参数、数据库、表、字段

手册里的连接机制:服务器把客户端发来的语句从 character_set_client 转换到 character_set_connection;带 introducer 的字面量(_utf8mb4、_latin2)由 introducer 决定字符集。collation_connection 只对字面量字符串比较有意义,字符串与列值比较时列自己的 collation 优先级更高。character_set_results 决定返回给客户端的字符集,覆盖结果数据、列名元数据、错误消息;设成 NULL 或 binary 则不做转换。

要看的变量

SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\_%' OR Variable_name LIKE 'collation%';

重点看这几个,都应该落在 utf8mb4:

  • character_set_client
  • character_set_connection
  • character_set_database
  • character_set_results
  • character_set_server
  • collation_connection

不用关心的:character_set_filesystem、character_set_system(存元数据用,一直是 utf8)、character_sets_dir。

连接建立后确认服务端是否采纳:

SELECT @@character_set_client;

SET NAMES 与防止客户端覆盖

SET NAMES utf8mb4;
SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci';

SET NAMES 一次设定 client / connection / results 三个,第二种写法同时设连接 collation。

几个边界报错:

  • SET character_set_client='ucs2' → ERROR 1231 (42000): Variable 'character_set_client' can't be set to the value of 'ucs2'
  • SET NAMES 'bogus' → ERROR 1115 (42000): Unknown character set: 'bogus'
  • SET NAMES 'utf8mb4' COLLATE 'bogus' → ERROR 1273 (HY000): Unknown collation: 'bogus'
  • mysql --default-character-set=bogus → Character set '...' is not a compiled character set;客户端认识但服务器不认识时,服务器回退默认字符集

用 my.cnf 固定下来:

[mysqld]
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

[client]
default-character-set = utf8mb4

[mysql]
default-character-set = utf8mb4

character-set-client-handshake = FALSE 让服务端忽略客户端握手时声明的字符集,避免被覆盖。也可以加 init_connect = 'SET NAMES utf8mb4',对所有非超级用户连接生效。

按实例 → 库 → 表 → 字段改

优先级从高到低。

实例:character_set_server = utf8mb4。RDS 在控制台改参数,重启生效。

ALTER DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

ALTER TABLE table_name
  MODIFY column_name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL;

ALTER TABLE `reactions`
  CHANGE `emoji` `emoji` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL;

查对象当前的字符集:

SELECT character_set_name FROM information_schema.COLUMNS
  WHERE table_name='user' AND column_name='displayname';

SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES;

SHOW CREATE DATABASE db1;
SHOW CREATE TABLE user;
SHOW FULL COLUMNS FROM user;

驱动侧

PHP mysqli:

$conn->set_charset("utf8mb4");
$conn->query("SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'");

DSN 里对应的键是 char_set,值用 'utf8mb4'。

Python:

mysql.connector.connect(..., charset='utf8mb4', collation='utf8mb4_unicode_ci')

SQLAlchemy:

mysql+pymysql://root@localhost/mydb?charset=utf8mb4

Go:

charset=utf8mb4&collation=utf8mb4_unicode_ci&parseTime=True&loc=Local

JDBC:用 Connector/J 5.1.13+。连接串不推荐配 characterEncoding,容易和驱动默认值冲突,可以用 useUnicode=true。

latin1 里存 UTF-8 的双重编码

latin1 列里如果已经存了 UTF-8 字节,直接 CONVERT TO utf8mb4 会把这些字节当成 latin1 再编码一次(double encoding)。得先把列改成 BINARY/VARBINARY 再 CONVERT,或者做字节级转换。

LOAD DATA INFILE 显式指定字符集:

LOAD DATA INFILE '/tmp/data.csv' INTO TABLE users
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n';

改完之后

utf8 → utf8mb4 对现有数据兼容,BMP 字符的存储方式相同,新增数据可以写 emoji。代价是存储空间预估要变:VARCHAR 按 4 字节/字符而不是 3 字节/字符算。

排查顺序:先确认列/表/库是不是 utf8mb4,再看 SHOW VARIABLES 里的 client / connection / results,最后看驱动连接串。ER_1366 基本都是某一环还停在 utf8,也就是 3 字节。

推荐文章

程序员茄子在线接单