MySQL中“关闭索引”在MyISAM和InnoDB中逻辑完全不同:MyISAM支持ALTER TABLE ... DISABLE/ENABLE KEYS(仅停非唯一索引,需配对使用),而InnoDB不支持该命令,应改用DROP/CREATE辅助索引、SET unique_checks=0或foreign_key_checks=0等替代方案。

MySQL 对“关闭索引”这件事,根本就分两套逻辑:MyISAM 和 InnoDB 完全不兼容,硬套同一套命令只会白忙活甚至报错。别信网上那些笼统说“ALTER TABLE ... DISABLE KEYS 就能提速”的教程——它只对 MyISAM 有效,InnoDB 表执行会直接返回 ERROR 1031 (HY000): Table storage engine for 't' doesn't have this option。
MyISAM 表怎么安全禁用/启用索引?
DISABLE KEYS 是 MyISAM 唯一真正生效的索引暂停机制,但它只停非唯一索引(PRIMARY KEY 和 UNIQUE KEY 仍强制校验),且必须配对使用 ENABLE KEYS 才能重建:
- 执行顺序必须是:
ALTER TABLE t DISABLE KEYS→ 批量插入 →ALTER TABLE t ENABLE KEYS -
ENABLE KEYS不是开关,而是全表扫描+排序建索引,状态显示Repair by sorting,耗时可能远超插入本身 - 别在高峰期跑
ENABLE KEYS,磁盘%util会飙到 100%,且临时索引文件可能占原数据 1.5 倍空间 - 空表导入不用
DISABLE KEYS——MyISAM 本来就是先写数据再建索引
InnoDB 表根本没有 DISABLE KEYS,该怎么做?
InnoDB 没有“暂停索引更新”这回事,它的二级索引是实时维护的。DISABLE KEYS 在 InnoDB 上要么报错,要么静默忽略。真正有效的替代动作是:
- 删掉非主键、非唯一、非外键依赖的辅助索引:
DROP INDEX idx_created_at ON t(导入完再CREATE INDEX) - 临时关唯一性检查:
SET unique_checks = 0(前提:你确认数据无重复,否则后续写入会出错) - 临时关外键检查:
SET foreign_key_checks = 0(仅当你确认子表数据合法,且没强一致性要求) - 主键和唯一索引绝不能删或关——删了
INSERT会因违反约束失败,关了unique_checks也只跳过重复校验,不跳过主键结构维护
为什么 DROP INDEX 后重建反而比 DISABLE KEYS 更可控?
对 InnoDB 来说,DROP INDEX + CREATE INDEX 是更透明、可预期的操作路径:
-
DROP INDEX立即释放 B+ 树内存和磁盘空间,插入时完全避开索引写放大 -
CREATE INDEX走的是排序构建(sort-based),比逐行插入时的随机页分裂高效得多,且 MySQL 5.7+ 支持 online DDL,可减少锁表时间 - 不会像
ENABLE KEYS那样卡在不可中断的单线程重建上,你能看到进度、能 kill、能预估耗时 - 注意:
DROP INDEX会锁表(尤其大表),务必选低峰期操作;而CREATE INDEX如果加ALGORITHM=INPLACE(5.6+)可避免全表复制
LOAD DATA INFILE 和批量 INSERT 的索引策略差异
就算你把索引全关了,不同导入方式对索引的“感知”也完全不同:
-
LOAD DATA INFILE:绕过 SQL 解析层,直接走存储引擎接口,InnoDB 会自动启用 bulk insert buffer 加速索引构建——此时关unique_checks效果明显,但DROP INDEX反而没必要 - 多值
INSERT INTO ... VALUES (),(),():仍走 SQL 层,每批插入都要触发索引节点分裂。这时删辅助索引收益最大,尤其是表有 3 个以上二级索引时,索引维护开销常占总耗时 70%+ - ORM 的
executemany()默认不生成多值语句,Python 需显式拼接,Java JDBC 需加rewriteBatchedStatements=true参数,否则还是单条提交
真正容易被忽略的点是:导入后不做 ANALYZE TABLE,查询性能会断崖下跌——索引统计信息过期,优化器选错执行计划,比插入慢更难排查。

















