是,MySQL ALTER TABLE 默认全表重建并锁表;5.6+支持ALGORITHM=INPLACE但受限于操作类型与版本,8.0.12+对VARCHAR增长度支持快速DDL;绕过锁表可用pt-online-schema-change或gh-ost等工具。

ALTER TABLE 修改字段时 MySQL 会锁表吗?
会,而且默认是全表重建(ALGORITHM=COPY),期间写入阻塞、磁盘 IO 暴涨、主从延迟飙升。5.6+ 支持 ALGORITHM=INPLACE,但仅限部分操作——比如改 DEFAULT 值或增删 COMMENT 通常可原地完成;而修改 INT 到 BIGINT、VARCHAR(255) 到 VARCHAR(500) 这类涉及行格式变更的操作,仍大概率触发拷贝重建。
- 用
SHOW PROCESSLIST能看到状态为altering table,且持续时间与表大小正相关 -
INFORMATION_SCHEMA.INNODB_TRX可查到长事务阻塞 DDL 的线索 - MySQL 8.0.12+ 对
VARCHAR长度增加(非缩短)支持快速 DDL,但前提是没用ROW_FORMAT=COMPACT且字段不在索引中
怎么判断某个 ALTER 操作是否走 INPLACE?
执行前加 EXPLAIN FORMAT=JSON 不生效,得靠执行时看 performance_schema.table_lock_waits_summary_by_table 或更直接:加 ALGORITHM=INPLACE, LOCK=NONE 强制指定,MySQL 会报错告诉你“不支持”。例如:
ALTER TABLE orders MODIFY COLUMN amount DECIMAL(19,4) ALGORITHM=INPLACE, LOCK=NONE;
如果报错 ALGORITHM=INPLACE is not supported for this operation,说明必须降级为 COPY 或换方案。
- 字段类型兼容性很关键:同精度数字类型扩宽(如
TINYINT→SMALLINT)通常支持INPLACE;跨大类(如TEXT→VARCHAR)一定不支持 -
LOCK=NONE要求无唯一索引冲突、无外键引用、且 MySQL 版本 ≥ 5.6.17 - 线上大表建议先在从库试跑,观察
SHOW ENGINE INNODB STATUS中的DATA DICTIONARY和LOG区域压力
不锁表的替代方案有哪些?
真要改字段又不能停写,只能绕开原生 ALTER:用影子表 + 数据迁移 + 原子切换。工具如 pt-online-schema-change 或 gh-ost 是工业级解法,但要注意它们本身也吃资源。
-
pt-online-schema-change依赖触发器,高并发下可能拖慢源表写入;触发器逻辑若出错会导致数据不一致 -
gh-ost用 binlog 解析代替触发器,对源表影响小,但要求binlog_format=ROW且binlog_row_image=FULL - 手动实现需注意:新旧表字段映射、NULL 默认值补全、自增 ID 冲突、外键约束暂时禁用(
FOREIGN_KEY_CHECKS=0) - 切换瞬间用
RENAME TABLE orders TO orders_old, orders_new TO orders,原子性由 MySQL 保证,但应用层要做好连接重连和幂等处理
为什么改完字段后查询反而变慢了?
常见于扩大字段长度(如 VARCHAR(100) → VARCHAR(500))后,MySQL 优化器误判索引选择性,或导致隐式类型转换失效。更隐蔽的是 ROW_FORMAT 变更引发页分裂加剧。
- 检查执行计划是否用了索引:
EXPLAIN SELECT ...看key和rows字段有无突变 - 对比
SHOW INDEX FROM orders中Cardinality值,若显著下降,需ANALYZE TABLE orders - 字段扩宽后若被用于
ORDER BY或GROUP BY,临时表可能从内存转磁盘,查Created_tmp_disk_tables状态变量确认 - 某些 ORM(如 Laravel Eloquent)会缓存表结构,改完字段要清空
schema:cache:clear类命令
真正卡住的从来不是语法对不对,而是你没意识到 ALTER 在 InnoDB 层触发的是 B+ 树重建、undo 日志膨胀、buffer pool 冲刷——这些不会报错,但会让 DBA 在凌晨三点盯着 iostat -x 1 发呆。



















