应使用LENGTH(col) > 400查询utf8mb4下VARCHAR(100)字段实际字节超长的值,因utf8mb4单字符最多占4字节,理论最大字节数为100×4=400,LENGTH返回字节数,CHAR_LENGTH返回字符数,二者差异可识别含emoji或生僻字的隐式超限数据。

查出 utf8mb4 下实际字节超长的字段值
MySQL 的 utf8mb4 编码下,一个字符最多占 4 字节,但 CHAR 或 VARCHAR 定义长度时按“字符数”算,不是字节数。当存入 emoji 或某些生僻汉字(如「?」)时,单字符占 4 字节,很容易突破列定义的字节上限(比如 VARCHAR(255) 在 utf8mb4 下最多存 1020 字节,但若全为 4 字节字符,只能存 255 个——看起来没问题;可一旦表用的是 utf8 而实际插入了 utf8mb4 字符,或字段被隐式转换,就可能触发截断或报错)。真正要揪出“看似合法、实则超字节”的数据,得绕过字符计数,直接看 LENGTH()。
-
LENGTH(col)返回字节数,CHAR_LENGTH(col)返回字符数;两者差值大,说明含多字节字符 - 对
VARCHAR(100)字段,执行SELECT * FROM t WHERE LENGTH(col) > 100 * 4是错的——因为 100 是字符上限,不是字节上限;正确逻辑是:如果该列定义为VARCHAR(100)且使用utf8mb4,理论最大字节数就是 400,所以应查LENGTH(col) > 400 - 注意:
TEXT类型不受此限制,但TINYTEXT/MEDIUMTEXT有自身字节上限,同样适用LENGTH()检查
识别因 mb4 导致的隐式截断风险
MySQL 5.5.3+ 默认支持 utf8mb4,但老表可能仍用 utf8(实际是 utf8mb3),而客户端连接用了 utf8mb4。此时插入 4 字节字符会静默截断(取决于 SQL_MODE),或报错 ERROR 1366 (HY000): Incorrect string value。要提前发现这类隐患,不能只看当前数据是否报错,得模拟写入行为。
- 检查字段实际字符集:
SHOW CREATE TABLE t看列定义里的CHARACTER SET,别信连接层设置 - 用
CONVERT(col USING utf8mb4)强转后比对长度:WHERE CHAR_LENGTH(CONVERT(col USING utf8mb4)) != CHAR_LENGTH(col)可找出原编码下无法完整表示的字符串(比如 latin1 存了中文,转 utf8mb4 后长度翻倍) - 重点查
ENUM和SET类型——它们内部以字节存储,且不支持 utf8mb4 多字节字符,存 emoji 必然失败
用 HEX() 定位具体异常字节序列
光知道某字段超长还不够,得定位是哪个字符惹的祸。MySQL 的 HEX() 函数能暴露原始字节,结合 UTF-8 编码规则(如 F0 9F 98 81 是 ? 的 UTF-8 四字节序列),可快速判断是否含非预期多字节字符。
- 查前 10 个超长记录的十六进制:
SELECT id, HEX(col), LENGTH(col) FROM t WHERE LENGTH(col) > 400 LIMIT 10 - 常见危险字节开头:
F0(4 字节 emoji)、E0(3 字节 CJK)、C2/C3(带重音的拉丁字符);若看到ED开头的三字节序列,可能是损坏的 UTF-8(如从 GBK 错转而来) - 避免在应用层拼接
HEX()结果做判断——它返回字符串,比较效率低;应先用LENGTH()过滤,再用HEX()辅助分析
修复前务必确认 ROW_FORMAT 和 innodb_large_prefix
想把字段从 VARCHAR(255) 扩到 VARCHAR(500) 来容纳更多字节?别急。InnoDB 表的单行最大长度受 ROW_FORMAT 和配置影响,尤其老版本 MySQL 默认 ROW_FORMAT=COMPACT 且 innodb_large_prefix=OFF,会导致索引键前缀限制在 767 字节——这意味着即使你改了列长度,建索引时仍可能报错 ERROR 1071 (42000): Specified key was too long。
- 先查当前设置:
SELECT ROW_FORMAT FROM INFORMATION_SCHEMA.INNODB_TABLES WHERE TABLE_NAME = 't',并确认SHOW VARIABLES LIKE 'innodb_large_prefix' - 安全做法:升级到 MySQL 5.7+,设
innodb_file_format=Barracuda,innodb_large_prefix=ON,再用ALTER TABLE t ROW_FORMAT=DYNAMIC - 临时绕过:把长字段移到
TEXT类型,或拆分到单独关联表——比硬扩VARCHAR更可靠
字符编码相关的长度问题,从来不是单纯改个类型就能解决的。真正麻烦的是那些已经在线上跑了三年、没报过错、但某天突然因一条 emoji 数据崩掉的字段——它们往往藏在 CHAR 类型里,或者被 CONVERT 隐式转换过多次,字节和字符数早已对不上。


















