bigywater.tech
MySQL / InnoDB · 44

不可见索引

同一个索引,只切换INVISIBLE和VISIBLE,看优化器是不是真的完全无视了它,以及不删索引也能做到的'软下线'为什么比直接DROP更安全。

14倍
索引不可见后,真实的全表扫描比走索引慢的倍数
0次重建
切换可见性全程不需要重建索引

做的事:同一个索引,只切换可见/不可见

amount列上已经有一个真实索引idx_amount,用ALTER TABLE切换它的可见性,索引本身不删除也不重建。

代码ALTER TABLE orders ALTER INDEX idx_amount INVISIBLE; -- 索引真实还在,只是优化器默认不再考虑用它 ALTER TABLE orders ALTER INDEX idx_amount VISIBLE; -- 改回可见,索引不需要重建

验证:可见时走索引,不可见时真实退化成全表扫描

实测 · 本机EXPLAIN FORMAT=TREE输出
索引可见:
-> Index range scan on orders using idx_amount over (490.00 <= amount <= 495.00) (cost=237 rows=526)

索引不可见:
-> Filter: (orders.amount between 490 and 495) (cost=5124 rows=5488)
    -> Table scan on orders (cost=5124 rows=49396)   ← 完全无视idx_amount,退化成全表扫描

索引标记成INVISIBLE之后,优化器在选执行计划的时候真实把这个索引当成不存在——EXPLAIN里连提都不提idx_amount,直接走了全表扫描。这个操作不涉及删除或重建索引,索引数据在磁盘上原封不动,后台的INSERT/UPDATE/DELETE依然会真实继续维护它,只是查询优化阶段真实不再把它纳入候选。

验证:真实耗时差了约14倍,用optimizer_switch还能强制用回来

实测 · 本机执行耗时(同样返回526行)
索引可见,走idx_amount:    1332  微秒 (约1.33毫秒)
索引不可见,走全表扫描:   18504  微秒 (约18.5毫秒)   ← 慢了约13.9倍

SET SESSION optimizer_switch='use_invisible_indexes=on';
-- 索引依然标记为INVISIBLE,但这个开关打开后:
-> Index range scan on orders using idx_amount ...   ← 又真实用回索引了
这14倍的差距真实来自"扫描范围"的差异:索引可见时,MySQL通过idx_amount的B+树直接定位到amount在490~495之间的526行,只读需要的那一小部分;索引不可见后退化成全表扫描,真实要挨个读完全部约5万行再逐行过滤。INVISIBLE这个特性真实的用途是上线前的"软删除"演练——先把一个怀疑没用的索引标记成不可见,观察一段时间业务是否受影响,如果没问题再真正DROP掉;如果发现某个查询意外变慢了,不需要重建索引,直接改回VISIBLE就行,比"先删索引、发现出问题再重建"这套流程安全得多,重建大表上的索引本身也是一个真实很耗时的操作。

分步演示:可见、不可见、强制启用不可见索引

每一步都是本文上面用EXPLAIN和真实计时跑出来的原始结果。

不删索引也能真实关掉它——INVISIBLE的三种状态
这一步新验证的状态

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),5万行orders,amount列索引idx_amount
    • ALTER TABLE ... ALTER INDEX ... INVISIBLE/VISIBLE
    • optimizer_switch='use_invisible_indexes=on':不改索引状态也能强制启用
    京ICP备2026067502号-1京公网安备11010502063487号