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

MySQL 5.7 中修改 VARCHAR 字段长度是否锁表,不取决于你写了多长的数字,而取决于这个字段在当前字符集下**实际占用的字节数是否跨越了 255 字节边界**。跨了就锁,没跨且是扩大长度,才可能不锁。
为什么 VARCHAR(60) → VARCHAR(64) 会锁表?
因为 MySQL 存储 VARCHAR 时,需要额外用 1 或 2 字节记录实际长度:
- utf8mb4 下,1 个字符最多占 4 字节;
VARCHAR(60)最多占 60 × 4 = 240 字节 → 用 1 字节存长度 -
VARCHAR(64)最多占 64 × 4 = 256 字节 → 必须用 2 字节存长度 - 长度字节数从 1 变成 2,InnoDB 无法 in-place 完成,只能走
ALGORITHM=COPY:建临时表、全量拷贝、重建索引、原子重命名 - 这个过程全程持有 MDL 写锁和表级写锁,所有 DML(INSERT/UPDATE/DELETE)和部分 DDL 都被阻塞
ALTER TABLE ... MODIFY 加 ALGORITHM=INPLACE 为什么还是锁?
ALGORITHM=INPLACE 不是免死金牌,它只对满足条件的操作生效:
- 扩大长度:仅支持「0–255 字节内」或「≥256 字节内」的变更;从 240 字节→256 字节属于跨档,直接报错:
ERROR 0A000: ALGORITHM=INPLACE is not supported - 缩小长度:任何缩小操作都不支持 in-place,强制
ALGORITHM=COPY - 即使语法允许加
ALGORITHM=INPLACE,MySQL 也会在执行前校验可行性;失败后自动降级或报错,不会静默退化 - 真正是否 inplace,得看
EXPLAIN FORMAT=JSON输出里的"alter_algorithm": "inplace"和"supports_inplace": true
怎么查当前字段实际字节数并预判锁表风险?
别凭经验猜 VARCHAR(255) 或 VARCHAR(256),要算字节:
- 查表字符集:
SHOW CREATE TABLE your_table;看DEFAULT CHARSET - utf8mb4:1 字符 ≤ 4 字节;utf8(真 utf8,非 alias):≤ 3 字节
- 计算:新长度 × 单字符最大字节数;旧长度 × 单字符最大字节数
- 若旧 ≤ 255 且新 > 255 → 必锁;若新 ≤ 255 且旧 ≤ 255 → 可能不锁;若两者都 ≥ 256 → 可能不锁(但需验证)
- 快速验证命令:
ALTER TABLE t MODIFY c VARCHAR(N) ALGORITHM=INPLACE, LOCK=NONE;;如果报错或状态变成copy to tmp table,说明 fallback 了
生产环境真要改,绕不开锁,怎么办?
核心思路不是“避免锁”,而是“不让锁影响业务”:
- 先杀长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60;,干掉卡住 MDL 的源头 - 调低
lock_wait_timeout(比如设为 5 秒),让 DDL 失败快、不卡住后续查询 - 大表必须用
pt-online-schema-change:它不依赖 MySQL 原生 DDL,靠触发器+影子表+chunk 拷贝,全程无表级锁 - 注意:pt-osc 要求主键存在、不能有外键、DDL 期间禁止直接改原表(否则触发器漏数据)
最常被忽略的一点:字符集和排序规则会影响实际字节数,而不仅仅是字段定义里的数字。改之前不换算字节,光看 VARCHAR(N) 的 N,等于蒙眼开车。











