MySQL在一亿数据的表添加索引的庖丁解牛

张开发
2026/4/16 13:11:16 15 分钟阅读

分享文章

MySQL在一亿数据的表添加索引的庖丁解牛
在 MySQL 中对一张一亿行数据的表执行ALTER TABLE ... ADD INDEX曾经是一个**“运维噩梦”**锁表数小时、主从延迟爆炸、业务停摆。但在 MySQL 5.6 引入Online DDL特别是 8.0 引入Instant DDL (部分场景)和并行索引构建后情况有了质的飞跃。然而“能加”不代表“无感”。添加索引的本质是**“全表扫描 排序 B 树构建”**这是一个极度消耗CPU和IO的过程。对于一亿行数据如果操作不当依然会导致磁盘 IO 打满、CPU 100%进而拖垮整个数据库实例。一、底层机制索引是如何“长”出来的添加索引不仅仅是修改元数据它需要物理地重组数据。1. 核心三步走无论什么版本添加二级索引Secondary Index通常包含三个步骤全表扫描 (Table Scan)读取聚簇索引主键的所有叶子节点数据因为二级索引需要包含主键值。代价一亿行数据的顺序读IO 吞吐量巨大。排序 (Sort)将读取到的数据按照新索引定义的列进行排序。代价内存不足时会使用磁盘临时文件排序Filesort极慢。构建 B 树 (Build Tree)将排序好的数据写入新的索引页构建平衡树结构。代价大量的随机写操作。2. Online DDL 的魔法 (MySQL 5.6)原理不再锁表复制整张表。创建一个空的临时索引树。扫描主键树将现有数据填入临时索引。关键点在扫描过程中如果有新的INSERT/UPDATE/DELETE发生MySQL 会将这些变更记录在Row Log中。最后阶段短暂锁表仅几秒到几分钟取决于变更量将 Row Log 中的增量应用到新索引然后原子切换。优势允许并发读写DML业务不中断。劣势IO 和 CPU 负载依然极高。虽然不锁表但可能因为资源争抢导致正常业务查询变慢抖动。3. MySQL 8.0 的进化并行构建 (Parallel Index Creation)利用多核 CPU 并行扫描和排序速度提升数倍。降序索引支持原生支持(col DESC)无需反向存储优化特定排序场景。不可见索引 (Invisible Index)允许先创建索引但不让优化器使用用于“灰度测试”索引效果确认无误后再设为可见。 核心洞察添加索引的本质是“用空间换时间用现在的 IO 换取未来的查询速度”。对于一亿数据这个过程就是一次高强度的“数据搬运与重组”必须小心控制流量。二、版本能力与执行模式特性MySQL 5.6 / 5.7MySQL 8.0影响算法INPLACE(在线)INPLACE并行构建8.0 速度快 3-5 倍锁表情况准备和提交阶段短暂锁写同左但时间更短8.0 业务感知更低特殊功能无不可见索引 (VISIBLE/INVISIBLE)可安全验证索引效果Instant 支持❌ 不支持❌不支持加索引(Instant 仅限部分加/改字段)加索引必须重建注意即使是 MySQL 8.0添加索引也不支持ALGORITHMINSTANT。它仍然需要扫描全表并构建数据结构。所谓的“快”是相对于旧版本的优化而非秒级完成。三、高危陷阱为什么容易翻车1. 主从复制延迟 (Replication Lag) ——最大杀手现象主库因为有多个线程或更强的 IO可能 30 分钟建完。但从库尤其是单线程回放的老版本可能需要 3 小时。后果在这 3 小时内从库严重滞后。如果业务读写分离依赖从库会导致读取不到最新数据甚至拖垮从库 IO。对策必须在从库上也使用 Online DDL且最好限制主库的执行速度以匹配从库。2. 缓冲池污染 (Buffer Pool Pollution)机制构建索引需要扫描全表这会将大量冷数据原本不在内存的数据强行加载到 Buffer Pool。后果原本热点的业务数据如用户信息、配置项被挤出内存。操作结束后正常业务查询命中率暴跌数据库出现长时间的性能“低谷期”。3. 磁盘空间爆炸需求构建过程中需要临时文件排序用和新索引文件。风险如果磁盘剩余空间小于表大小 索引大小操作会失败并回滚不仅浪费时间还可能产生大量碎片。4. 长事务阻塞 (MDL Lock)场景DDL 在最后切换阶段需要获取 MDL 写锁。风险如果此时有一个长事务如未提交的大查询正在运行DDL 会无限等待。而在等待期间所有对该表的后续访问都会被阻塞引发雪崩。四、最佳实战策略如何优雅地“提速”面对一亿数据严禁直接在业务高峰期执行原生ALTER。策略 A使用pt-online-schema-change或gh-ost(最稳健)这是互联网行业的标准答案。它们通过创建影子表来规避原生 DDL 的风险。原理创建不含索引的影子表。在影子表上添加索引小表很快。后台分批拷贝旧数据到影子表并通过 Trigger 或 Binlog 同步增量。原子交换表名。优势可控性可以设置--max-load当数据库负载高时自动暂停拷贝保护业务。无主从延迟影子表在从库上也是逐步应用的不会造成瞬间的巨大延迟。随时取消发现问题可随时终止不影响原表。命令示例 (pt-osc)pt-online-schema-change\--userroot--passwordxxx--host127.0.0.1\--alterADD INDEX idx_email (email)\--execute\Dmydb,tusers\--max-lag5s\--chunk-size1000策略 BMySQL 8.0 原生 不可见索引 (次选需低峰期)如果你使用的是 MySQL 8.0且确信从库性能足够可以采用原生方式但必须配合不可见索引特性。创建不可见索引ALTERTABLEusersADDINDEXidx_email(email)ALGORITHMINPLACE,LOCKNONE,VISIBLENO;此时索引已建成但优化器不会使用它。观察与验证观察几天看是否有慢查询可以利用该索引通过EXPLAIN手动指定验证。监控数据库负载确保没有异常。设为可见ALTERTABLEusersALTERINDEXidx_email VISIBLE;这一步是元数据操作秒级完成瞬间生效。优势避免了“建好索引发现没用”或者“建索引过程把库搞挂”的风险。如果建的过程中出问题直接DROP即可不影响业务。策略 C业务低峰期 限流 (保底方案)如果不能用工具只能用原生时间选择凌晨 3:00 - 5:00。参数调优设置innodb_sort_buffer_size(8.0) 增大排序内存。监控Threads_running一旦过高立即KILL掉 DDL 进程Online DDL 支持断点续传吗不支持杀了得重来所以慎杀最好是预防。从库保护先在从库执行确认延迟可控后再在主库执行或者利用主从并行特性。 总结一亿数据加索引全景图维度核心挑战推荐方案关键动作性能全表扫描 排序IO/CPU 爆表pt-osc / gh-ost限流避免拖垮业务锁表最后阶段 MDL 阻塞Online DDL (LOCKNONE)检查并杀掉长事务主从从库回放慢延迟巨大工具同步或8.0 并行监控Slave_Lag验证建完发现无效或负优化MySQL 8.0 不可见索引先INVISIBLE验证后VISIBLE空间临时文件 新索引占双倍空间清理磁盘确保剩余空间 2 倍表大小终极心法在一亿数据面前添加索引不是简单的 SQL 命令而是一次资源调度战役。原生 DDL 是“大力出奇迹”虽快但猛易伤及无辜业务抖动在线工具是“太极推手”以柔克刚通过分流和限速在业务无感中完成重构。MySQL 8.0 的不可见索引则是“试金石”让索引的效果验证变得安全可控。于重建中见秩序于限速中见平稳以工具为盾解 IO 之牛于大规模运维中求无感之真。行动指令首选工具只要条件允许一律使用pt-online-schema-change或gh-ost不要迷信原生ALTER。8.0 特权如果是 MySQL 8.0务必先用VISIBLENO创建验证无误后再上线。空间检查操作前df -h确保磁盘空间充足防止中途失败。长事务清理操作前检查information_schema.innodb_trx确保无长事务。监控报警操作期间重点监控Disk IOPS,CPU Usage,Slave_Delay。思维升级将“加索引”视为一个过程而非动作。关注过程中的资源竞争和数据一致性而不仅仅是结果。这就是MySQL 一亿数据表添加索引”于扫描中见负载于构建中见智慧以限流为策解性能之牛于数据演进中求平滑之真。

更多文章