MySQL 5.7 默认走重建表流程,锁表是常态:创建临时表→全量拷贝数据→重建索引→原子重命名,全程持有MDL_EXCLUSIVE锁,阻塞所有DML和SELECT。

MySQL 5.7 默认走重建表,锁表是常态
5.7 对绝大多数 ALTER TABLE 操作(比如改字段类型、加索引、改字符集)不支持真正的就地修改,而是强制执行「重建表」流程:创建临时表 → 全量拷贝数据 → 重建索引 → 原子重命名。这个过程全程持有 MDL_EXCLUSIVE 锁和表级写锁,任何 SELECT、INSERT、UPDATE、DELETE 都会被阻塞。
常见踩坑点:
-
VARCHAR(255) → VARCHAR(1024)看似只是扩大长度,但若触发存储格式变更(如长度头从1字节升为2字节),5.7 就会退化为重建 -
ADD COLUMN last_login DATETIME NOT NULL DEFAULT NOW()在非空表上执行,需全表回填默认值,无法跳过物理扫描 -
SHOW PROCESSLIST中大量线程卡在Waiting for table metadata lock,不是网络或IO问题,是 MDL 被长事务占着不放
MySQL 8.0.12+ 支持部分操作无锁,但有严格前提
8.0.12 开始对极少数操作真正支持无锁 DDL,比如末尾 ADD COLUMN(且无 NOT NULL DEFAULT)、某些索引添加。但它不会“自动降级”——不满足条件就直接报错,不会静默切回重建模式。
验证是否真走无锁,必须用:
EXPLAIN FORMAT=JSON ALTER TABLE t ADD COLUMN x INT;
检查输出中是否有:
"alter_algorithm": "inplace""supports_inplace": true"supports_online": true
漏掉任一条件,就不是你想象中的“在线”。
ALGORITHM=INPLACE 和 LOCK=NONE 不等于“不锁表”
这两个参数是提示 MySQL 尽量走轻量路径,但是否生效取决于操作类型、表定义、版本能力三者交集。
典型失效场景:
-
ADD UNIQUE INDEX标称支持 inplace,但存在重复值校验或含BLOB字段时可能降级 -
MODIFY COLUMN类型变更在 5.7 中 100% 强制重建;8.0.12+ 仅当长度头不变、字符集不变、无存储格式升级才可能 inplace -
LOCK=NONE在 5.7 中只对加普通索引有效,且要求无长事务、无外键、无全文索引
ENGINE 变更(MyISAM → InnoDB)始终锁表,别信“在线”宣传
无论哪个版本,ALTER TABLE ... ENGINE=InnoDB 都要重建表。5.6+ 虽优化为“拷贝-替换”,但最后仍需短暂加锁切换元数据;大表操作可能持续数分钟甚至小时,业务写入必然卡住。
更麻烦的是隐含兼容性问题:
-
ERROR 1071 (42000): Specified key was too long:InnoDB 默认页大小下,UTF8MB4字段索引超 767 字节 -
ERROR 1067 (42000): Invalid default value for 'created_at':严格模式下'0000-00-00'不被接受 - 带
FULLTEXT的TEXT字段,旧版 InnoDB 不支持,转换直接中断
真正难的从来不是语法能不能跑通,而是锁住业务那几分钟里,谁来扛住上游重试、下游积压、监控告警连环炸。


















