MySQL 5.7在线修改VARCHAR长度并非完全不可行,而是受InnoDB存储格式硬约束:仅支持0→255或256→更大的增大(不跨1/2字节边界),跨边界(如255→256)或任何缩小均强制ALGORITHM=COPY全表重建并锁表,且长事务会加剧MDL等待。

MySQL 5.7 的在线 DDL 并非“无法修改列长度”,而是对 VARCHAR 长度变更有严格限制——它只允许特定方向、特定范围的“增大”,且任何“缩小”都强制退化为全表重建(ALGORITHM=COPY)。这不是 bug,是 InnoDB 存储格式决定的硬约束。
为什么增大 VARCHAR 长度有时仍会锁表?
关键在长度字节数是否变化:InnoDB 用 1 字节记录 ≤255 字符的 VARCHAR 实际长度,用 2 字节记录 ≥256 字符的长度。跨这个边界(如 VARCHAR(255) → VARCHAR(256))必须重写所有行数据。
- 支持真·在线(
LOCK=NONE)的增大:从 0→255、或从 256→更大(如 500→1000) - 不支持在线的增大:从 ≤255 → ≥256(如 255→256、100→300),报错
ERROR 0A000: ALGORITHM=INPLACE is not supported - 字符集影响实际字节数:utf8mb4 下,100 字符最多占 400 字节,但判断依据仍是“字符数是否跨越 255”,不是字节数
为什么缩小 VARCHAR 长度一定锁表?
缩小操作无法仅改元数据——InnoDB 必须逐行检查并截断超出新长度的数据,还要更新每行的长度头和页内偏移。这只能通过 ALGORITHM=COPY 完成,全程持有 MDL_EXCLUSIVE 锁。
- 哪怕只是
MODIFY COLUMN c VARCHAR(200) VARCHAR(199),也会触发全表拷贝 - 执行时若存在长事务(哪怕只是未提交的
SELECT),DDL 会卡在Waiting for table metadata lock状态,而非报错 -
lock_wait_timeout控制此等待,默认 31536000 秒(一年),所以你会看到“不动”,而不是“超时”
怎么确认一次 ALTER 真没锁表?
别信 Query OK,要看并发行为和系统视图:
- 执行前查长事务:
SELECT * FROM information_schema.innodb_trx WHERE TIME > 60;,手动KILL掉 - 执行中开另一个会话持续写入:
INSERT INTO your_table VALUES (...);,观察是否被阻塞 - 查进度:
SELECT * FROM information_schema.innodb_alter_table;—— 若有记录且state是copy to tmp table,说明已退化;若无记录,大概率是真INPLACE
真正容易被忽略的是:即使你写了 ALGORITHM=INPLACE, LOCK=NONE,MySQL 也会根据列定义、字符集、当前表状态自动降级。最稳妥的方式,是对大表统一走 pt-online-schema-change,它绕开了 MDL 锁瓶颈,但代价是磁盘空间翻倍和触发器双写风险。


















