bigywater.tech
常见报错 · 4

MySQL ERROR 1406 Data too long for column:长度按字符算,关掉严格模式就变成悄悄截断,加宽列跨过 255 字节要重建表

“Data too long for column” 是写入数据时最常见的报错之一。它到底按字符还是按字节算?关掉严格模式后会发生什么?TEXT、varchar 的上限到底多大?遇到它之后是该加宽列,还是该改数据?加宽一个大表的列会不会锁表?这篇在本机逐个实测。

5 个
5 个 emoji(20 字节)能放进 varchar(5):长度按字符算,6 个汉字(18 字节)才超出
1265
把 sql_mode 设为空之后,超长的值不报 1406,只留这个 Warning 并被截成前 5 个字符
1 ms → 271 ms
20 万行表加宽 varchar:不跨 255 字节的 INPLACE 是 1 ms;跨过 255 字节只能 COPY,271 ms

报错文本与长度单位

实测 · 本机执行输出
  insert into s(v) values ('abcdef') -> DataError: (1406, "Data too long for column 'v' at row 1")
  insert into s(v) values ('ok'),('abcdefg'),('ok') -> DataError: (1406, "Data too long for column 'v' at row 2")
  表里的行数: 0(整条语句都没写进去)
  5 个英文字母: char_length=5 字节(utf8mb4)=5 -> ok
  5 个汉字: char_length=5 字节(utf8mb4)=15 -> ok
  5 个 emoji: char_length=5 字节(utf8mb4)=20 -> ok
  6 个汉字: char_length=6 字节(utf8mb4)=18 -> DataError: (1406, "Data too long for column 'v' at row 1")

varchar(5) 按字符数限制,和每个字符占几字节无关;多行 INSERT 出错时 at row N 是语句内的行号,整条语句回滚。

sql_mode 决定报错还是截断

实测 · 本机执行输出
  默认 @@sql_mode = ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
  set session sql_mode = '' 之后 insert 'abcdefghij' -> ok,影响行数 1
  show warnings: (('Warning', 1265, "Data truncated for column 'v' at row 1"),)
  表里存的是: (('abcde',),)(被截成前 5 个字符)
  只留 STRICT_TRANS_TABLES 再试一次 -> DataError: (1406, "Data too long for column 'v' at row 1")

严格模式把超长变成 1406,sql_mode 为空时变成 Warning 1265 并截断存储。截断是数据损坏,不是修复。

相邻的几个报错

实测 · 本机执行输出
  tinyint 放 200 -> DataError: (1264, "Out of range value for column 'a' at row 1")
  int unsigned 放 -1 -> DataError: (1264, "Out of range value for column 'b' at row 1")
  create table (v varchar(20000)) -> OperationalError: (1074, "Column length too big for column 'v' (max = 16383); use BLOB or TEXT instead")
  create table (v varchar(16383)) -> ok
  create table (v varchar(16384)) -> OperationalError: (1074, "Column length too big for column 'v' (max = 16383); use BLOB or TEXT instead")
  text 放 70000 个英文字母 -> DataError: (1406, "Data too long for column 't' at row 1")
  text 放 65535 个英文字母 -> ok
  text 放 21846 个汉字(65538 字节)-> DataError: (1406, "Data too long for column 't' at row 1")
  mediumtext 放 70000 个英文字母 -> ok

数值溢出是 1264;varchar 声明超过可容纳的行大小是 1074(最大 16383);TEXT 的限制按字节算,汉字占 3 字节所以放得更少。

缩列前先自查

实测 · 本机执行输出
  各行 char_length: ((1, 5), (2, 17), (3, 20))
  alter table sh modify v varchar(10) -> DataError: (1265, "Data truncated for column 'v' at row 2")
  事先自查:select count(*) from sh where char_length(v) > 10 = 2
  表结构没变: `v` varchar(20) DEFAULT NULL,

ALTER 缩短列在严格模式下会因为已有数据过长而失败(1265),表保持原样。先用 char_length 数出会超的行。

加宽列的代价

实测 · 本机执行输出
  表里 200000 行
  varchar(10) -> varchar(20)(utf8mb4 最大字节数 40 -> 80) ALGORITHM=INPLACE: ok,1 ms
  varchar(20) -> varchar(40)(utf8mb4 最大字节数 80 -> 160) ALGORITHM=INSTANT: OperationalError: (1845, 'ALGORITHM=INSTANT is not supported for this operation. Try ALGORITHM=COPY/INPLACE.'),0 ms
  varchar(60) -> varchar(63)(utf8mb4 最大字节数 240 -> 252) ALGORITHM=INPLACE: ok,1 ms
  varchar(63) -> varchar(100)(utf8mb4 最大字节数 252 -> 400) ALGORITHM=INPLACE: OperationalError: (1846, 'ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.'),0 ms
  varchar(63) -> varchar(100)(utf8mb4 最大字节数 252 -> 400) ALGORITHM=COPY: ok,271 ms
  现在的列定义: ['`v10` varchar(20) DEFAULT NULL,', '`v60` varchar(100) DEFAULT NULL,']
  (varchar 最大字节数 ≤ 255 时用 1 字节存长度,> 255 用 2 字节;改的前后落在不同的一档,就不能原地改,必须重建整张表)

varchar 最大字节数越过 255 时长度前缀的大小会变,INPLACE 不能做,必须 COPY 重建。同一档内加宽几乎是瞬间。

分步演示

数据超长
本步的对象

    本文用到的工具与边界

    • 本机 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 实例(默认 sql_mode,库字符集 utf8mb4),PyMySQL 的 unix socket 连接;ALTER 的耗时是 20 万行小表上的单次测量,数字只说明量级
    • 没有验证:MySQL 5.7 / 8.0 / 8.4 上的文本和限制;长度前缀由 1 字节变 2 字节就是 INPLACE 不能做的原因(只验证了边界两侧的行为);ALGORITHM=COPY 期间表上的锁与并发写入的表现;大表(上亿行)的加宽耗时;pt-online-schema-change / gh-ost;其它 sql_mode 组合(例如 STRICT_ALL_TABLES、TRADITIONAL);JDBC / ORM 层的校验
    京ICP备2026067502号-1京公网安备11010502063487号