MySQL默认对“Data too long”采取静默截断(非严格模式)或中断报错(严格模式),均不优雅;真正可控需应用层预检(用LENGTH()或字节长度计算)+数据库层显式控制(开STRICT_TRANS_TABLES、正确扩字段并验字符集、分批插入避坑)。

直接结论:别指望数据库自动“优雅”兜底,Data too long 报错时,MySQL 默认行为是截断(若非严格模式)或中断(若开启 STRICT_TRANS_TABLES),两者都不算优雅——前者丢数据不报警,后者整批失败。真正可控的处理,必须在应用层预检 + 数据库层显式控制。
查清字段真实字节长度,别信字符数
中文、emoji、特殊符号在 utf8mb4 下占 3–4 字节,CHAR_LENGTH() 返回字符个数,LENGTH() 才返回真实字节数。报错前先验证:
- 执行
SELECT LENGTH('测试'), LENGTH('?'), LENGTH('a')—— 结果分别是6、4、1 - 若字段定义为
VARCHAR(100),插入含 30 个 emoji 的字符串,CHAR_LENGTH()是 30,但LENGTH()可能超120,直接触发错误 - 应用层必须用等效方式预检:Python 中用
len(s.encode('utf8')),Java 中用s.getBytes(StandardCharsets.UTF_8).length
避免依赖 SUBSTRING() 截断,优先改结构或开严格模式
SUBSTRING(col, 1, N) 看似简单,但它按字符数截,不是字节数;且只解决“进得去”,不解决“对不对”。更关键的是:
- 如果没开
STRICT_TRANS_TABLES,MySQL 会静默截断并只发Warning,你根本收不到报错 - 执行
SELECT @@sql_mode,确认返回值包含STRICT_TRANS_TABLES;否则整批插入可能一半成功一半丢数据而不自知 - 想扩字段?不能只写
ALTER TABLE t MODIFY c VARCHAR(500),必须显式声明字符集:ALTER TABLE t MODIFY c VARCHAR(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci - 改完立刻验证:
SHOW FULL COLUMNS FROM t LIKE 'c',确认Collation列含utf8mb4_前缀
批量插入时分片 + 分批提交,绕过 max_allowed_packet 和锁竞争
单条多值 INSERT 虽快,但含大文本时极易撞上 max_allowed_packet(默认通常 4MB)。与其硬调服务端参数,不如从源头拆解:
- 把一批数据按每
100–500行切片,每片单独事务提交;Navicat 要关掉“使用事务”,启用“每 X 行提交一次” - 导出 SQL 时选“每行一条 INSERT”,别用“多值 INSERT”格式,避免工具拼出超长语句
- 命令行导入不用
mysql -e "INSERT ...",改用mysql db_name ,并确保文件是 UTF-8 无 BOM - 若字段确实要存超大文本(如日志、HTML),考虑用
TEXT或MEDIUMTEXT类型,并确认连接与表都设为utf8mb4,否则LENGTH()算出来的字节数会翻倍
最常被忽略的一点:前缀索引。哪怕你把 VARCHAR(255) 改成 VARCHAR(500),只要该字段上有 INDEX(col(100)),插入时仍按前 100 字节校验——超长部分不入库也不报错,但唯一性、排序全乱。上线前务必跑一遍 SHOW INDEX FROM 表名 WHERE Column_name = '字段名',看 Sub_part 是否大于 0。










