MySQL 8.0の不可視インデックス(Invisible Index)でインデックスを安全に削除する手順

「このインデックス、もう使ってないかも?」と思いながら、いきなり DROP INDEX するのは怖いものです。月次バッチでだけ使われている、特定の管理画面だけが叩いている、といった見落としがあると本番障害に直結します。

MySQL 8.0 の 不可視インデックス(Invisible Index) は、この「削除前の安全な検証」を実現するための機能です。

不可視インデックスとは

オプティマイザがクエリ実行時に そのインデックスを存在しないものとして扱う 機能です。

特徴:

  • オプティマイザは使わない(実行計画から消える)
  • メンテナンスは継続される(INSERT/UPDATE/DELETE で同期され続ける)
  • 構文1つで即座に切り替えられる(VISIBLEINVISIBLE)

「使わないけど、すぐ戻せる状態」にできるのがポイントです。

構文

-- 不可視にする(オプティマイザが使わなくなる)
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 が効くケースはあるが、サイズによっては実用にならない)
  • 再作成中はその列に対するクエリが遅くなる
  • 再作成中の INSERTUPDATEDELETE の応答時間が悪化する

不可視インデックス機能があれば、削除を「メタデータ変更」だけで擬似的に試せるので、こうしたリスクをほぼ完全に避けられます。

未使用インデックス検出と組み合わせる

実務的には「未使用インデックスを自動検出 → 不可視化して観察 → 削除」の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 などで代替する

参考

タグ: MySQL , PostgreSQL , パフォーマンスチューニング