bigywater.tech
常见报错 · 5

MySQL ERROR 1366 Incorrect string value:不只是 emoji,连接字符集错配时汉字不报错却变乱码

Incorrect string value: '\xF0\x9F\x98\x80' for column 是国内最常被搜的 MySQL 报错之一,标准答案“改成 utf8mb4”并不总能解决问题。这篇在本机把几种情形分开复现:列字符集不够、连接字符集错配、老表索引长度限制。

1366
utf8mb3 的列里放 4 字节字符(emoji、生僻字 𠮷、带肤色 emoji)报的错误码;汉字和 € 是 3 字节,不会报
767
COMPACT 行格式的老表 convert 成 utf8mb4 时撞上的索引长度上限(字节);先改成 DYNAMIC 行格式就能转
453 ms
10 万行、带 varchar(255) 索引的表 convert to character set utf8mb4 的耗时

1366 的文本

实测 · 本机执行输出
  服务器默认: character_set_server=utf8mb4,collation_server=utf8mb4_0900_ai_ci
  建表用 charset=utf8,实际: ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3
  insert 一个 emoji (U+1F600) -> DataError: (1366, "Incorrect string value: '\\xF0\\x9F\\x98\\x80' for column 'v' at row 1")
  insert 'a😀b' -> DataError: (1366, "Incorrect string value: '\\xF0\\x9F\\x98\\x80b' for column 'v' at row 1")
  insert 汉字 -> ok
  表里的行: ((1, '汉字'),)

MySQL 的 utf8 是 utf8mb3 的别名,每字符最多 3 字节。报错里 \xF0\x9F\x98\x80 是出问题的字符的 UTF-8 字节,从坏字符开始一直到值末尾。

不止 emoji

实测 · 本机执行输出
  gbk      每字符最多 2 字节  默认排序规则 gbk_chinese_ci
  latin1   每字符最多 1 字节  默认排序规则 latin1_swedish_ci
  utf8mb3  每字符最多 3 字节  默认排序规则 utf8mb3_general_ci
  utf8mb4  每字符最多 4 字节  默认排序规则 utf8mb4_0900_ai_ci
  英文 a                   UTF-8 1 字节  utf8mb3: ok  utf8mb4: ok
  汉字 汉                   UTF-8 3 字节  utf8mb3: ok  utf8mb4: ok
  欧元符号 €                 UTF-8 3 字节  utf8mb3: ok  utf8mb4: ok
  生僻字 𠮷 (U+20BB7)        UTF-8 4 字节  utf8mb3: 1366  utf8mb4: ok
  emoji 😀                UTF-8 4 字节  utf8mb3: 1366  utf8mb4: ok
  带肤色的 emoji 👍🏽          UTF-8 8 字节  utf8mb3: 1366  utf8mb4: ok

凡是 UTF-8 编码 4 字节的字符(emoji、部分生僻汉字)在 utf8mb3 列里都会 1366;汉字和 € 是 3 字节,没有问题。

连接字符集错配

实测 · 本机执行输出
  列字符集     连接 SET NAMES              abc              汉              😀
  latin1   utf8mb4                    原样         报 1366         报 1366
  latin1   latin1                     原样       变样:'汉'      变样:'😀'
  latin1   gbk                        原样         报 1366         报 1366
  utf8mb3  utf8mb4                    原样             原样         报 1366
  utf8mb3  latin1                     原样       变样:'汉'      变样:'😀'
  utf8mb3  gbk                        原样         报 1406        变样:'馃榾'
  utf8mb4  utf8mb4                    原样             原样             原样
  utf8mb4  latin1                     原样       变样:'汉'      变样:'😀'
  utf8mb4  gbk                        原样         报 1406        变样:'馃榾'
  gbk      utf8mb4                    原样             原样         报 1366
  gbk      latin1                     原样         报 1366         报 1366
  gbk      gbk                        原样         报 1366        变样:'馃榾'

错配时只有一部分组合会报 1366,另一些组合写入成功但内容已经变成乱码(utf8mb4 列经 latin1 连接写入汉字,读回 汉)。同一个报错背后可能是列,也可能是连接。

把表转成 utf8mb4

实测 · 本机执行输出
  表 big:100000 行,row_format=DYNAMIC,v varchar(255) 带普通索引 k(v)
  alter table big convert to character set utf8mb4 -> ok,453 ms
  之后: ) ENGINE=InnoDB AUTO_INCREMENT=100001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;emoji 现在能写进去: ok
  索引占的最大字节数: varchar(255) × 3 = 765,× 4 = 1020
  建 COMPACT 老表 (utf8mb3): ok;行格式 Compact
  alter table cpt convert to character set utf8mb4 -> OperationalError: (1071, 'Specified key was too long; max key length is 767 bytes')
  先 alter table cpt row_format=dynamic -> ok;再 convert -> ok

CONVERT TO CHARACTER SET utf8mb4 会重建整张表。DYNAMIC 行格式下直接成功,COMPACT 行格式的老表索引上限是 767 字节,255×4 = 1020 超了,先改行格式。

确认连接字符集

实测 · 本机执行输出
  pymysql 不传 charset: ('utf8mb4', 'utf8mb4', 'utf8mb4')
  pymysql charset=utf8mb4: ('utf8mb4', 'utf8mb4', 'utf8mb4')
  pymysql charset=latin1: ('latin1', 'latin1', 'latin1')
  utf8mb4 连接上执行 set names latin1 之后: ('latin1', 'latin1', 'latin1')

character_set_client、character_set_connection、character_set_results 三个变量要一致。一条 SET NAMES 能改掉整个会话,连接池的初始化语句尤其要检查。

分步演示

4 字节字符
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / MySQL 26.10.0 与 Redis 8.10.2(Homebrew,每个实验自己起实例,端口 9150 以上,只监听 127.0.0.1,不碰本机已有的实例)/ Python 3.15.0 + PyMySQL(按各篇注明用到哪些及版本);报错文本都是真实复现时原样捕获的;计时用 time.perf_counter,数字每次运行会有小幅抖动。本篇用一个自己起的 MySQL 26.10.0 实例(character_set_server=utf8mb4),PyMySQL 的 unix socket 连接;矩阵里的“连接字符集”是用 SET NAMES 设置的,客户端发出的字节始终是 UTF-8
    • 没有验证:真正使用 latin1 / gbk 编码的客户端(这里客户端始终发 UTF-8 字节);MySQL 5.7 / 8.0 上报错文本里列名的写法(这里只有 for column 'v' 这种形式);JDBC 的 characterEncoding / connectionCollation 参数与 Connector/J 行为;mysqldump 导出再导入时的字符集处理;排序规则从 utf8mb3_general_ci 变成 utf8mb4_0900_ai_ci 后对查询结果与索引的影响;大表(千万行以上)转换的耗时与在线 DDL 工具
    京ICP备2026067502号-1京公网安备11010502063487号