2026年5月20日
2026年5月20日
MySQL の OPTIMIZE TABLE と ANALYZE TABLE の使い方|InnoDB での実際の動作、効果がある場面、実行前の確認点
OPTIMIZE TABLE は、削除や更新を重ねて断片化したテーブルを作り直し、未使用領域を減らすための MySQL の文です。ただし WordPress で標準の InnoDB テーブルでは、名前から想像する「最適化」ではなくテーブルの再構築+統計情報の更新として動きます。この記事では MySQL 8.4(2026年10月時点の LTS 版)の動作を前提に、効果がある場面と安全な実行手順を説明します。
OPTIMIZE TABLE がすること
構文は次のとおりです。テーブルはカンマ区切りで複数指定できます。
OPTIMIZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name [, tbl_name] ...
ストレージエンジンによって中身が変わります。
| エンジン | 実際の動作 |
|---|---|
| InnoDB | ALTER TABLE ... FORCE に置き換えられ、テーブルを再構築してインデックス統計を更新する。innodb_file_per_table が有効なら、空いた領域が OS に返る |
| MyISAM | 削除・分割された行の修復、インデックスページの並べ替え、統計の更新を行う |
| その他 | 多くは「対応していない」というメッセージを返す |
InnoDB のテーブルに実行すると、次のように note が出ます。これはエラーではなく、再構築と ANALYZE を代わりに実行したという意味です。最後の行が status OK なら成功です。
+-------------+----------+----------+-------------------------------------------------------------------+
| Table | Op | Msg_type | Msg_text |
+-------------+----------+----------+-------------------------------------------------------------------+
| wp.wp_posts | optimize | note | Table does not support optimize, doing recreate + analyze instead |
| wp.wp_posts | optimize | status | OK |
+-------------+----------+----------+-------------------------------------------------------------------+
必要な権限は対象テーブルへの SELECT と INSERT です。
効果がある場面・ない場面
効果が見込めるのは次のような場合です。
- 大量の行を削除した後(例: 投稿リビジョン、期限切れの transient、スパムコメントをまとめて消した後)
- 可変長の列(
longtextなど)を持つテーブルで更新を大量に繰り返した後 innodb_file_per_tableが有効で、ディスク容量を OS に返したい場合
一方、日常的に定期実行する必要はあまりありません。InnoDB はもともとページを 93% 程度までしか埋めないため、ある程度の空きは正常です。削除や更新が少ないテーブルに実行しても、ほとんど変わりません。
なお、innodb_file_per_table が無効で、テーブルがシステムテーブルスペース(ibdata1)に入っている場合は、再構築しても OS にディスク容量は返りません。現在の設定は次で確認できます(MySQL 5.6.6 以降の既定値は 1=有効)。
SELECT @@innodb_file_per_table;
実行前に未使用領域を確認する
information_schema.TABLES の DATA_FREE で、テーブルごとのおおよその未使用領域を見られます。ただし MySQL 8.0 以降はこの表の統計がキャッシュされる(information_schema_stats_expiry の既定値は 86400 秒)うえ、InnoDB の統計自体も削除直後には更新されていないことがあります。正しい値を見るには、先に統計を更新し、キャッシュを無効にしてから調べます。
-- このセッションだけキャッシュを使わない
SET SESSION information_schema_stats_expiry = 0;
-- 対象テーブルの統計を更新(軽い処理)
ANALYZE TABLE wp_posts, wp_postmeta, wp_options;
-- 未使用領域が大きい順に表示(MB)
SELECT TABLE_NAME,
ENGINE,
ROUND(DATA_LENGTH / 1024 / 1024, 1) AS data_mb,
ROUND(INDEX_LENGTH / 1024 / 1024, 1) AS index_mb,
ROUND(DATA_FREE / 1024 / 1024, 1) AS free_mb
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
ORDER BY DATA_FREE DESC;
DATA_FREE は目安の値です。サーバーにログインできる場合は、データディレクトリにある テーブル名.ibd のファイルサイズを実行前後で比べるほうが確実です。
参考として、MySQL 8.4.13 の検証環境で 30,000 行(1行約2KB)の InnoDB テーブルから 3 分の 2 の行を削除し、OPTIMIZE TABLE を実行したところ、.ibd ファイルは約 76MB から約 31MB に縮みました。削除だけではファイルは小さくならず、再構築して初めて OS に容量が返ります。
実行手順
1. バックアップを取る
再構築はテーブルを作り直す処理です。失敗に備えて、事前にデータベースのバックアップを取ります。WordPress なら WP-CLI で次のように出力できます。
wp db export backup-before-optimize.sql
2. 空き容量を確かめる
InnoDB の再構築では、新しいテーブルを作ってから古いものと入れ替えます。対象テーブルと同程度の空きディスク容量を用意してください。大きなテーブルほど、実行前に df -h などで確認しておくと安全です。
3. 実行する
-- 1つのテーブル
OPTIMIZE TABLE wp_posts;
-- 複数のテーブル
OPTIMIZE TABLE wp_posts, wp_postmeta, wp_comments;
テーブル接頭辞(wp_)はサイトによって違います。wp-config.php の $table_prefix で確認してください。
4. 結果を確認する
各テーブルの最後の行が status OK になっていれば完了です。Msg_type が error の行があれば、その内容を確認します。前の節の SQL やファイルサイズで、未使用領域が減ったことを確かめます。
ロックと負荷について
InnoDB の通常のテーブルとパーティションテーブルでは、オンライン DDL で再構築されます。テーブル全体を排他ロックするのは準備段階と確定段階の短い時間だけで、その間以外は読み書きを続けられます。
ただし、次の場合はテーブルコピー方式になり、処理中の書き込みが待たされます。
- テーブルに
FULLTEXTインデックスがある old_alter_tableが有効、またはサーバーが--skip-newで起動している
オンラインで動く場合でも、再構築はディスク I/O と CPU を使います。アクセスの少ない時間帯に、大きなテーブルから1つずつ実行するのが無難です。MyISAM など InnoDB 以外のエンジンでは、処理中ずっとテーブルがロックされます。
レプリケーションとバイナリログ
OPTIMIZE TABLE は既定でバイナリログに書かれ、レプリカでも同じ処理が実行されます。ソース側だけで実行したい場合は NO_WRITE_TO_BINLOG(別名 LOCAL)を付けます。この指定には OPTIMIZE_LOCAL_TABLE 権限も必要です。
OPTIMIZE NO_WRITE_TO_BINLOG TABLE wp_posts;
ANALYZE TABLE・REPAIR TABLE との違い
| 文 | 目的 | InnoDB での扱い |
|---|---|---|
OPTIMIZE TABLE |
再構築して未使用領域を減らす | 再構築+統計更新(上記) |
ANALYZE TABLE |
インデックス統計だけを更新する | 対応。軽い処理で、クエリの実行計画が不自然なときに使う |
REPAIR TABLE |
壊れたテーブルを修復する | 非対応。MyISAM・ARCHIVE・CSV 用 |
InnoDB で OPTIMIZE TABLE を実行すると統計も更新されるため、続けて ANALYZE TABLE を実行する必要はありません。また InnoDB のテーブルに REPAIR TABLE を実行しても「The storage engine for the table doesn't support repair」と返るだけです。InnoDB の不整合の確認には CHECK TABLE を使います。
FULLTEXT インデックスだけを最適化する
InnoDB の全文検索インデックスは、行を削除してもその場では古い語のデータが残ります。インデックスだけを整理したい場合は、innodb_optimize_fulltext_only を有効にしてから実行します。この設定中はテーブルの再構築は行われません。
SET GLOBAL innodb_optimize_fulltext_only = ON;
OPTIMIZE TABLE wp_posts; -- 全文インデックスが整理されきるまで繰り返す
SET GLOBAL innodb_optimize_fulltext_only = OFF;
1回に処理する語の数は innodb_ft_num_word_optimize で調整できます。SET GLOBAL には SYSTEM_VARIABLES_ADMIN などの権限が必要で、共用レンタルサーバーでは使えないことが多い点に注意してください。
WordPress では WP-CLI でまとめて実行できる
WP-CLI の wp db optimize は、wp-config.php の接続情報を使って mysqlcheck --optimize を実行し、データベース内の全テーブルに OPTIMIZE TABLE をかけます。
wp db optimize
# Success: Database optimized.
サーバーに mysqlcheck コマンドが入っている必要があります。全テーブルを順に処理するので、大きなサイトでは時間帯を選んでください。特定のテーブルだけを処理したい場合は、wp db query で SQL を直接渡します。
wp db query "OPTIMIZE TABLE wp_posts, wp_postmeta;"
不要なリビジョンを先に減らしておくと、再構築の効果が大きくなります。手順は「WordPressの投稿リビジョンをデータベースから削除する」を参照してください。WP-CLI の他のデータベース操作は「WP-CLIのデータベース操作コマンド」にまとめています。
まとめ
- InnoDB での
OPTIMIZE TABLEは「再構築+統計更新」。表示されるnoteは正常な動作です。 - 効果があるのは大量の削除・更新の後。定期的に全テーブルへ実行する必要はありません。
- 実行前に
ANALYZE TABLEとinformation_schema_stats_expiry = 0で未使用領域を確認し、バックアップと空きディスクを用意します。 FULLTEXTインデックスがあるテーブルはコピー方式になり、書き込みが待たされます。- InnoDB に
REPAIR TABLEは使えません。統計の更新だけならANALYZE TABLEで足ります。