alter table ... modify 是否安全取决于新旧长度是否跨越255字节边界:≤255字节用1字节存长度,≥256字节用2字节;utf8mb4下varchar(63)→(64)即越界触发copy;减小长度也强制copy,不支持inplace。

直接用 ALTER TABLE ... MODIFY 是最简方式,但“安全”与否取决于新旧长度是否跨越 255 字节边界——这是 MySQL 5.7 中真正决定锁表、耗时和失败风险的核心条件。
判断是否触发 COPY 算法(关键第一步)
MySQL 5.7 对 VARCHAR 长度变更是否能走 INPLACE,只看「存储该字段所需长度字节数」是否变化:小于等于 255 字节用 1 字节存长度,大于等于 256 字节用 2 字节。字符集直接影响字节数计算。
- 若当前字段是
VARCHAR(60),字符集为utf8mb4(每字符最多 4 字节),则最大可能占 60 × 4 = 240 字节 → 属于「≤255」区间 - 若要扩到
VARCHAR(100),100 × 4 = 400 字节 → 跨入「≥256」区间 →ALGORITHM=INPLACE被拒绝,必须用COPY - 若原字段是
VARCHAR(255)扩到VARCHAR(256),哪怕只+1,只要字符集是utf8mb4,255×4=1020 → 已超 255 字节;256×4=1024 → 仍属「≥256」区间 → 可 INPLACE - 反过来,
VARCHAR(63)(63×4=252)→VARCHAR(64)(64×4=256)就是典型越界点,必触发COPY
执行前必须验证的三项事实
别依赖经验或字段声明值,要用实际数据和元信息交叉确认:
- 查当前字符集:
SHOW CREATE TABLE your_table;看DEFAULT CHARSET和各列COLLATE - 算字节上限:用
SELECT CHARACTER_MAXIMUM_LENGTH, COLLATION_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='your_table' AND COLUMN_NAME='your_column';,再乘以该排序规则对应的最大字节/字符(utf8mb4= 4,utf8= 3,latin1= 1) - 确认是否已有长内容:
SELECT MAX(LENGTH(your_column)) FROM your_table;—— 如果当前最大值已接近新上限,说明业务确有扩容必要;如果全为短字符串,扩到 500 可能只是预防性动作,但 COPY 成本不变
选择执行方式:MODIFY 还是分步重建?
对中小表(行数 MODIFY 并显式指定算法;对大表或生产核心表,分步更可控:
- 小表且不越界(如
VARCHAR(50) → VARCHAR(100),latin1字符集):ALTER TABLE t MODIFY c VARCHAR(100);—— 几乎瞬时完成,DML 不中断 - 小表但越界(如
VARCHAR(60) → VARCHAR(100),utf8mb4):ALTER TABLE t MODIFY c VARCHAR(100) ALGORITHM=COPY, LOCK=SHARED;—— 允许并发读,写会被阻塞直到结束 - 大表(>1000 万行)或不能接受任何写阻塞:
gh-ost或pt-online-schema-change更稳妥;ALGORITHM=COPY在主库上会全程锁写,且临时空间消耗 ≈ 原表大小 - 避免用「ADD + UPDATE + DROP + CHANGE」四步法:它多一次全表扫描、多一列存储、且
CHANGE COLUMN在越界时同样 fallback 到 COPY,纯属冗余
最容易被忽略的坑:减少长度也强制 COPY
很多人只关注“扩容”,但 MySQL 5.7 中,**任何减小 VARCHAR 长度的操作都不支持 INPLACE**,一律走 COPY。比如从 VARCHAR(200) 改回 VARCHAR(100),即使仍在 255 字节内,也会锁表重写全表。
这意味着:如果误扩后想回退,代价和当初扩容一样高;线上做长度调整前,务必确认方向——增、减、还是保持同区间内微调。











