mysql 8.0 大表重建慢的核心矛盾是 ddl 卡在 prepare 或 rebuild 阶段,尤其 innodb_ddl_threads=1 时二级索引构建单线程;alter table ... add index 卡在 “altering table” 可能是长事务阻塞、i/o 打满或 change buffer 合并拖累,提速需设 innodb_ddl_threads≥4(仅对二级索引有效),主键重建永远无法并行。

MySQL 8.0 大表重建慢,核心矛盾不是“能不能在线”,而是“DDL 阶段卡在哪”——prepare 和 rebuild 阶段最容易假死,尤其当 innodb_ddl_threads 为默认值 1 时,二级索引构建完全单线程。
ALTER TABLE ... ADD INDEX 卡在 “altering table” 不动?先确认是不是真卡
SHOW PROCESSLIST 显示 altering table 并不等于阻塞;它可能正处在 Online DDL 的 prepare 阶段(锁 MDL、生成 row log)或 rebuild 阶段(扫描主键、排序构建索引)。常见误判点:
- 被长事务阻塞:查
performance_schema.data_lock_waits,看是否有其他事务持有TABLE或REC级锁 - I/O 打满:用
iostat -x 1观察%util是否持续 >95%,await在 SSD 上超过 20ms 就需警惕 - change buffer 合并拖累:临时设
SET GLOBAL innodb_change_buffering = 'none'(机械盘场景尤其有效)
想提速,必须开并行 —— 但只对二级索引生效
MySQL 8.0.27+ 才真正支持可控的 DDL 并行,靠的是 innodb_ddl_threads,不是 innodb_parallel_read_threads(后者只影响 SELECT 并行读)。
- 主键重建(
DROP PRIMARY KEY或ADD PRIMARY KEY)永远单线程,无法加速 - 二级索引(
ADD INDEX)才可能并行:设innodb_ddl_threads = 4(建议 ≤ CPU 核数),同时确保innodb_sort_buffer_size≥ 4M(太小导致频繁落盘,太大抢 buffer pool) - 并行效果受数据分布影响极大:若索引字段重复值极高(选择性
Online DDL 写入抖动大?这不是 bug,是 row log 机制决定的
Online DDL 不是无代价的“零停机”。它靠 row log 记录变更,在 rebuild 阶段回放。高并发写 + 频繁更新索引字段 → row log 快速膨胀 → 触发 flush/merge → 冲击 buffer pool 和 redo log。
- 监控关键指标:
Innodb_log_waits > 0持续出现,说明innodb_log_file_size过小或刷盘跟不上 - 临时缓解:建索引前动态调大
innodb_log_buffer_size(如从 16M → 64M),或重启调大innodb_log_file_size - 生产环境更稳妥的做法:避开高峰,或用
pt-online-schema-change分摊压力——但它会多占一倍磁盘空间,且延长主从延迟
建完索引查询仍慢?别急着改 SQL,先看统计信息和选择性
索引建成功 ≠ 查询走索引。大表上常见 EXPLAIN 显示 type: ALL,根源常在优化器误判:
- 统计信息陈旧:执行
ANALYZE TABLE tbl_name强制重采样(InnoDB 默认采样率低,千万级表极易误判) - 索引选择性崩坏:算
COUNT(DISTINCT col)/COUNT(*),低于 0.01 时优化器大概率弃用该索引 - 隐式类型转换:如
WHERE varchar_col = 123,哪怕有索引也会全表扫描
真正难处理的,是那些既不能并行(主键重建)、又无法跳过 prepare 阶段(锁 MDL)、还碰上 I/O 瓶颈的大表 DDL——这时候,pt-online-schema-change 不是“高级选项”,而是唯一能控制节奏的手段。











