mysql 5.7 中 varchar 长度修改触发全表重建,主因是长度头字节数变更(≤255字节用1字节,≥256字节需2字节),utf8mb4下汉字占4字节更易越界,且隐式not null/default或长事务会强制copy算法。

ALTER TABLE MODIFY COLUMN 在 MySQL 5.7 中触发全表重建,不是因为“改长度”这个动作本身有多重,而是它悄悄越过了 InnoDB 对「原地修改」的物理约束边界。
ALTER TABLE 触发 COPY 算法的字节头切换
MySQL 存储 VARCHAR 字段时,用 1 或 2 个字节记录实际内容长度:
- 长度 ≤ 255 字节 → 用 1 字节存长度
- 长度 ≥ 256 字节 → 必须用 2 字节存长度
这个「长度头字节数」是行格式的一部分,不可动态变更。所以:
-
VARCHAR(10)→VARCHAR(255):仍 ≤255 字节,长度头保持 1 字节 → 可INPLACE -
VARCHAR(10)→VARCHAR(256):长度头需从 1 字节升为 2 字节 → 必须重建整张表,即走ALGORITHM=COPY
你看到的报错 ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE 就是这个原因。
字符集让字节数判断更危险
utf8mb4 下,一个汉字或 emoji 占 4 字节。你以为 VARCHAR(100) 是 100 字符,实际最多占 400 字节 —— 已超 255,直接掉进 COPY 区间。
- 表字段原为
VARCHAR(50)(utf8mb4下最多 200 字节)→ 扩到VARCHAR(64)(256 字节)就踩线 - 查当前字段最大字节占用:
SELECT MAX(LENGTH(column_name)) FROM table_name;
别只看CHAR_LENGTH(),LENGTH()才反映真实存储字节数
隐式 NOT NULL 或默认值会绕过算法声明
即使你显式写了 ALGORITHM=INPLACE, LOCK=NONE,只要字段定义含 NOT NULL DEFAULT 'xxx' 且表非空,MySQL 就会强制回填默认值 → 触发全表扫描 → 降级为 COPY。
- 检查是否含隐式约束:
SHOW CREATE TABLE table_name - 若只是想扩长度,确保语句不含
NOT NULL或DEFAULT(哪怕原表已有,MODIFY时不重复声明即可)
大表上别信“理论上不锁表”
很多人看到官方文档写 “Extending VARCHAR column size: In Place = Yes”,就以为能秒完成。但现实是:
- 一旦落入
COPY模式,整个过程 = 创建临时表 + 全量拷贝 + 建索引 + 锁表重命名 -
SHOW PROCESSLIST里卡在copy to tmp table就是它 - 长事务会让
MDL写锁无限等待(lock_wait_timeout默认一年),业务查询全挂
真正安全的做法不是赌 INPLACE,而是提前用 pt-online-schema-change 或应用层双写迁移 —— 尤其当表行数 > 100 万、平均长度接近 255 字节时,那个「1 字节 → 2 字节」的切换点,往往就是线上抖动的起点。











