「このインデックス、もう使ってないかも?」と思いながら、いきなり DROP INDEX するのは怖いものです。月次バッチでだけ使われている、特定の管理画面だけが叩いている、といった見落としがあると本番障害に直結します。
MySQL 8.0 の 不可視インデックス(Invisible Index) は、この「削除前の安全な検証」を実現するための機能です。
不可視インデックスとは
オプティマイザがクエリ実行時に そのインデックスを存在しないものとして扱う 機能です。
特徴:
- オプティマイザは使わない(実行計画から消える)
- メンテナンスは継続される(INSERT/UPDATE/DELETE で同期され続ける)
- 構文1つで即座に切り替えられる(
VISIBLE⇔INVISIBLE)
「使わないけど、すぐ戻せる状態」にできるのがポイントです。
構文
-- 不可視にする(オプティマイザが使わなくなる)
ALTER TABLE users ALTER INDEX idx_xxx INVISIBLE;
-- 可視に戻す(オプティマイザが使うようになる)
ALTER TABLE users ALTER INDEX idx_xxx VISIBLE;
-- 状態確認
SELECT INDEX_NAME, IS_VISIBLE
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = 'your_schema' AND TABLE_NAME = 'users';
-- 不可視のまま新規作成することも可能
CREATE INDEX idx_xxx ON users(col) INVISIBLE;
安全な削除手順
不可視インデックスを使った削除手順は以下の通りです。
ステップ1: 不可視化
ALTER TABLE users ALTER INDEX idx_xxx INVISIBLE;
オプティマイザがこのインデックスを使わなくなります。実行計画は「インデックスが削除された場合と同じ」状態になります。
ステップ2: 一定期間モニタリング
不可視化した状態で本番運用を続け、以下を監視します。
- スロークエリログに新規クエリが出現しないか
- 既存の重要クエリの応答時間が悪化していないか
- パフォーマンス監視ツール(Datadog / New Relic / pt-query-digest 等)でアプリケーション全体への影響
期間は 使われる可能性のあるバッチサイクル+α が目安です。月次バッチがあるなら最低 1ヶ月。
ステップ3: 問題があれば即可視化に戻す
ALTER TABLE users ALTER INDEX idx_xxx VISIBLE;
不可視 → 可視への切り替えはメタデータの変更だけなので 即座に反映されます。インデックスの再構築は不要です。
ステップ4: 一定期間問題なければ削除
ALTER TABLE users DROP INDEX idx_xxx;
ここまでくれば「使われていないことを確認した上での削除」になり、リスクが大幅に下がります。
「いきなり DROP」の何が怖いか
DROP INDEX は逆向きが取れない操作です。削除後に「やっぱり必要だった」と気付いたら、再作成する必要があり、
- 大規模テーブルでは再作成自体が長時間ロックを取る(オンライン DDL が効くケースはあるが、サイズによっては実用にならない)
- 再作成中はその列に対するクエリが遅くなる
- 再作成中の
INSERT/UPDATE/DELETEの応答時間が悪化する
不可視インデックス機能があれば、削除を「メタデータ変更」だけで擬似的に試せるので、こうしたリスクをほぼ完全に避けられます。
未使用インデックス検出と組み合わせる
実務的には「未使用インデックスを自動検出 → 不可視化して観察 → 削除」の3段階運用がおすすめです。
-- ステップ0: performance_schema から未使用インデックス候補を抽出
SELECT object_name, index_name
FROM sys.schema_unused_indexes
WHERE object_schema = 'your_schema'
ORDER BY object_name, index_name;
注意点:
performance_schemaの統計情報は DB 起動時にリセット される。有効化してすぐに削除せず、ある程度ためてから判断する- 月次バッチでしか使わないインデックス が候補に出ることがある。バッチサイクルを跨いで観察する
- FOREIGN KEY 制約に必要なインデックス が出てくることがある。MySQL は外部キーに自動でインデックスを作るので、削除すると制約違反になる。事前に確認する
PostgreSQL の場合
PostgreSQL には不可視インデックス相当の機能はありません(2026年5月時点の認識)。代替手段:
-- ロックを最小化してインデックスを作成
CREATE INDEX CONCURRENTLY idx_xxx ON users(col);
-- ロックを最小化してインデックスを削除
DROP INDEX CONCURRENTLY idx_xxx;
CONCURRENTLY オプションは長時間のテーブルロックを取らずに DDL を実行できますが、「削除を試して問題があれば即戻す」という用途には向きません(削除 → 再作成のオーバーヘッドが発生するため)。
PostgreSQL で同等の効果を狙うなら、pg_hint_plan 拡張や pg_dbms_stats で そのインデックスを使わないようヒント を与え、しばらく観察してから DROP するアプローチになります。
まとめ
- 不可視インデックスは「オプティマイザは無視するがメンテは継続」という状態を作れる MySQL 8.0+ の機能
- 構文:
ALTER TABLE ... ALTER INDEX ... INVISIBLE / VISIBLE - 削除手順: 不可視化 → 観察(バッチサイクル含む)→ 問題なければ DROP
- いきなり DROP するより遥かに安全。切り戻しがメタデータ変更だけで完結
- 未使用インデックス検出(
sys.schema_unused_indexes)と組み合わせて運用するのが定石 - PostgreSQL には同等機能はなく、
pg_hint_planなどで代替する