mysql 5.7 修改 varchar 长度是否锁表,关键看实际字节数是否跨越255字节边界:utf8mb4下varchar(60)→(64)因240→256字节导致长度字节从1升为2,必须copy重建表并全程持表级写锁。

MySQL 5.7 修改 VARCHAR 长度是否锁表,关键看字节边界
不是看 VARCHAR(255) 还是 VARCHAR(256),而是算 utf8mb4 下实际字节数是否跨过 255 字节阈值。比如 VARCHAR(60) → VARCHAR(64):60×4=240 字节(用 1 字节存长度),64×4=256 字节(必须用 2 字节存长度),结构变更不可 in-place,必然触发 ALGORITHM=COPY 全表重建。
- 查当前字符集:
SHOW CREATE TABLE your_table,确认是utf8mb4(1 字符 ≤ 4 字节)还是utf8(≤ 3 字节) - 旧长度 × 最大单字符字节数 ≤ 255,且新长度 × 最大单字符字节数 > 255 → 必锁
- 两者都 ≤ 255 或都 ≥ 256 → 才可能走
INPLACE,但需验证 - 快速试探命令:
ALTER TABLE t MODIFY c VARCHAR(N) ALGORITHM=INPLACE, LOCK=NONE;,若卡在copy to tmp table或报错,说明 fallback 了
为什么加 ALGORITHM=INPLACE 还是锁表
ALGORITHM=INPLACE 不是开关,是声明;MySQL 会在执行前校验可行性,不满足就降级或报错。常见失效场景:
- 缩小字段长度(如
VARCHAR(100)→VARCHAR(50)):一律不支持 in-place,强制 COPY - 新增
NOT NULL DEFAULT字段且表非空:需回填默认值,必须全表扫描,无法跳过物理变更 - 字段含
BLOB/TEXT或存在外键/全文索引:supports_inplace返回 false - 真正是否生效,得看
EXPLAIN FORMAT=JSON输出里的"alter_algorithm": "inplace"和"supports_inplace": true
生产环境改大表,别赌“不锁”,要控影响范围
哪怕理论上可 INPLACE,只要表大、并发高、有长事务,LOCK=NONE 也大概率失败。核心不是避免锁,而是不让锁拖垮业务:
- 先清长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60;,干掉持有 MDL 读锁的源头 - 调低
lock_wait_timeout(比如设为 5 秒),让 DDL 失败快、不卡住后续查询 - 千万级以上表,放弃原生命令,直接上
pt-online-schema-change - 务必先跑
--dry-run:它只建影子表、不启触发器、不拷数据,能暴露权限、主键缺失、外键未处理等硬伤
pt-online-schema-change 执行时最易忽略的三个条件
工具再强,缺一不可。漏掉任一个,轻则中途失败,重则数据不一致:
- 目标表必须有**主键或唯一非空索引**:否则分块拷贝无法准确定位,
pt-osc直接拒绝执行 - 表不能已有触发器:工具自己要建
AFTER INSERT/UPDATE/DELETE触发器捕获增量,冲突即报错 - 外键需显式处理:要么用
--foreign-key-index指定索引名,要么加--alter-foreign-keys-method=auto让工具自动重建外键约束 - 参数不配好会出事:
--chunk-time=0.5控制每块拷贝耗时上限,--max-lag=5防从库延迟过大,--critical-load="Threads_running=50"避免高峰期压垮数据库
真实线上改表,最难的从来不是语法对不对,而是你没看到的那个未提交事务,或者那个被遗忘的、连着老系统还在跑的长连接。











