MySQL 5.6+中ALTER TABLE MODIFY COLUMN是否锁表取决于是否触发行格式重写:VARCHAR长度调整通常不锁表,但改类型、加NOT NULL等可能全表重建锁表。

ALTER TABLE MODIFY 会锁表吗?
在 MySQL 5.6 及以上版本(且启用 innodb_file_per_table 和 innodb_online_alter_log_max_size 合理),对 VARCHAR 字段执行 ALTER TABLE ... MODIFY 或 CHANGE **可能不锁表**,但前提是只增大长度、且目标长度 ≤ 255(或从 ≤255 扩到 ≤255)、字符集不变、不涉及行格式变更。一旦突破这些边界——比如从 VARCHAR(100) 改成 VARCHAR(300),或从 utf8mb3 切到 utf8mb4,InnoDB 就会触发 **full table rebuild**,整个过程阻塞 DML(INSERT/UPDATE/DELETE),只允许 SELECT(取决于隔离级别和版本)。
- MySQL 5.7+ 默认开启
ALGORITHM=INPLACE自动降级尝试,但你不能依赖它“一定成功”——需用SHOW CREATE TABLE确认当前行格式(ROW_FORMAT=DYNAMIC更友好)和字符集 - 执行前务必查
information_schema.INNODB_TRX,确认没有长事务,否则在线 DDL 会被阻塞并可能超时失败 - 错误日志里若出现
Waiting for table metadata lock,说明有活跃事务正在访问该表,不是 DDL 本身的问题,而是并发冲突
怎么安全地扩大 VARCHAR(255) 到 VARCHAR(500)?
这是最常踩坑的场景:表面只是“变长”,实际触发重建。因为 VARCHAR 在 255 以上时,InnoDB 需要额外 2 字节存储长度,且 utf8mb4 下最大单字符占 4 字节,存储结构变化导致无法原地更新。
- 先用
SELECT COUNT(*) FROM tbl_name评估数据量;若超千万行,别直接跑ALTER,考虑分批重写或使用 pt-online-schema-change - 显式指定算法和锁级别:
ALTER TABLE t MODIFY COLUMN c VARCHAR(500) ALGORITHM=INPLACE, LOCK=NONE;—— 如果报错ALGORITHM=INPLACE is not supported,说明必须重建,此时改用LOCK=SHARED(允许读,阻塞写)比默认更可控 - 监控
SHOW PROCESSLIST中该语句状态,若卡在altering table超过预期时间,立刻KILL并检查磁盘 I/O 和innodb_sort_buffer_size
为什么 DESC 表结构看不出锁风险?
DESC table_name 只显示字段定义,完全不反映底层存储约束。真正决定是否锁表的是:当前列的 CHARACTER SET、COLLATION、所在行的 ROW_FORMAT、以及目标长度是否跨越 255/65535 这两个临界点。
- 查真实存储信息:
SELECT TABLE_NAME, ROW_FORMAT, CREATE_OPTIONS FROM information_schema.TABLES WHERE TABLE_NAME = 't'; - 查字段细节:
SELECT COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME, COLUMN_TYPE FROM information_schema.COLUMNS WHERE TABLE_NAME = 't' AND COLUMN_NAME = 'c'; - 特别注意
COLUMN_TYPE返回的是varchar(255)还是varchar(255) utf8mb4_0900_as_cs—— 后者说明已启用了新排序规则,某些旧版本 DDL 兼容性更差
线上小表也别跳过预检
哪怕只有几百行,如果表上有外键、全文索引、虚拟列或触发器,MODIFY 仍可能意外退化为 COPY 算法。MySQL 不会在语法校验阶段提前报错,而是在执行中才拒绝。
- 用
EXPLAIN FORMAT=JSON ALTER TABLE t MODIFY COLUMN c VARCHAR(500);(MySQL 8.0.12+)可预判执行计划,看"alter_algorithm": "inplace"是否存在 - 测试库必须和生产库版本、参数(尤其是
innodb_strict_mode)一致,否则STRICT模式关闭时允许的隐式转换,在线上会直接报错中断 - 备份不是万能的——
mysqldump导出再导入必然锁表,且丢失 GTID 位置;真正安全的兜底是xtrabackup的热备快照
锁表与否不取决于“改多大”,而取决于“MySQL 底层是否能复用现有页结构”。很多 DBA 在开发环境试过没问题,上线就挂,就是因为没比对过 information_schema 里那几行关键元数据。


















