独立サイトのデータベース最適化:スロークエリログ、インデックス、テーブル構造のチューニング

WordPressのデータベース最適化は、まずスロークエリログを有効にして問題を特定し、次にEXPLAINで実行計画を分析し、最後にインデックス追加やテーブル構造の変更を判断する順序が正しい。スロークエリのしきい値は1秒に設定し、毎日TO...

数据库优化四步:先定位再动手DB tuning in 4 steps: locate first, tune second wp_postmeta是WordPress最大的性能黑洞,80%慢查询和它有关wp_postmeta is WP’s biggest bottleneck; 80% slow queries hit it ① 开慢日志① Enable slow log long_query_time=1long_query_time=1s ② EXPLAIN分析② EXPLAIN the SQL 看type和rowsCheck type & rows ③ 加索引③ Add indexes 复合索引优先Composite first ④ 改表结构④ Schema fix 拆postmetaSplit postmeta 慢查询阈值:超过1秒的SQL记入慢日志,每天看TOP 10Slow threshold: log queries >1s; review daily TOP 10 EXPLAIN里type=ALL就是全表扫描,必须加索引;rows越大越危险type=ALL means full table scan; rows=越大越危险 索引不是越多越好:写多读少的表加索引会拖慢写入More indexes != better; write-heavy tables slow down wp_postmeta加(meta_key,meta_value)复合索引,查询能快10倍以上(meta_key,meta_value) composite index speeds up 10x+ 反直觉:加索引不是万能药,加错了反而更慢Counter-intuitive: indexes can slow you down if mis-added 每个索引都占磁盘、拖慢INSERT/UPDATE,wp_options这种写多的表别乱加Indexes cost disk and slow INSERT/UPDATE; don’t over-index write-heavy tables 正确流程:慢日志定位 → EXPLAIN确认 → 加索引 → 对比前后耗时Right flow: log → EXPLAIN → index → compare before/after 不要凭直觉加索引,每次都用EXPLAIN验证执行计划Never guess; verify execution plan with EXPLAIN every time
WordPressデータベース最適化の4ステップ:スロークエリログで特定、EXPLAINで分析、インデックス追加、テーブル構造変更

独立サイトのデータベース最適化:スロークエリログ、インデックス、テーブル構造のチューニング

結論から言うと:WordPressのデータベース最適化は、まずスロークエリログを有効にして問題を特定し、次にEXPLAINで実行計画を分析し、最後にインデックス追加やテーブル構造の変更を判断する順序が正しい。スロークエリのしきい値は1秒に設定し、毎日TOP 10を確認する。wp_postmetaテーブルはWordPress最大のパフォーマンスの黑洞で、スロークエリの約80%がこれに関係しており、(meta_key,meta_value)の複合インデックスを追加すると関連クエリが10倍以上速くなる。直感に反する判断:インデックスは多ければ多いほど良いわけではない。インデックスはそれぞれディスクを消費し、書き込みを遅くする。書き込みが多く読み取りが少ないテーブルにやたらとインデックスを追加すると、かえって遅くなる。感覚でインデックスを追加せず、毎回EXPLAINで実行計画を検証すること。

1. WordPressのデータベースはなぜ遅くなるのか

WordPressはMySQLをデータベースとして使用しており、デフォルトのテーブル構造は小規模サイトなら問題なく動作するが、コンテンツが増えるとどんどん遅くなる。遅さの根源はサーバー設定ではなく、2つの設計上の問題にある。wp_postmetaテーブルはすべてのカスタムフィールドを1つのキーバリューテーブルに保存するため、1つの商品のパラメータを調べるのに複数回のJOINが必要になる。wp_optionsテーブルはすべての設定項目を混在させているため、管理画面を読み込むたびに全テーブルをスキャンすることになる。

1つのWordPressページは平均12〜20回データベースにクエリを実行し、その大部分はwp_postmetaの検索である。プラグインを10個インストールし、各プラグインがwp_postmetaにカスタムフィールドを書き込むと、このテーブルは数十万行に膨れ上がる可能性がある。この状態でmeta_queryを含むページリクエストが来ると、数十万行をスキャンすることになり、スロークエリが自然と発生する。

よくあるスロークエリの発生源 関連テーブル 典型的な症状 割合
カスタムフィールドで記事を絞り込む wp_postmeta 商品絞り込みページの読み込みが3秒を超える 約40%
管理画面でプラグイン設定を読み込む wp_options 管理画面で保存するたびに数秒間白画面になる 約20%
タグ・カテゴリのアーカイブページ wp_term_relationships 記事が増えるとアーカイブページが遅くなる 約15%
コメントとユーザーテーブルのクエリ wp_comments/wp_users コメント一覧やユーザー管理が遅い 約10%
プラグイン自体の複雑なクエリ プラグインが独自に作成したテーブル 特定の機能ページが特に遅い 約15%

データベースが遅くなると、ページキャッシュがある程度隠してくれる。ページキャッシュがヒットしているときはデータベースにクエリを実行しない。しかしキャッシュミス時(記事を公開した直後、キャッシュされていないページへのアクセス)には、スロークエリが露呈する。そのためデータベース最適化とキャッシュ戦略は合わせて確認する必要がある。詳細は独立サイトの3層キャッシュ戦略を参照。データベースがすでにCore Web Vitalsに影響を与えるほど遅くなっている場合、最適化の優先順位はCore Web Vitalsの最適化。

2. スロークエリログ:遅いSQLの見つけ方

データベースを最適化する第一歩はインデックスを追加することではなく、どのSQLが遅いのかを突き止めることです。目視で当て推量しても分かりません。あるクエリが遅いと思い込んでいても、実際は別のクエリかもしれません。MySQLにはスロークエリログが標準で備わっており、有効にすると閾値を超えたSQLがすべて記録されます。

MySQLの設定(my.cnfまたはmy.ini)に3行追加します。slow_query_log=1、long_query_time=1、slow_query_log_file=/var/log/mysql/slow.log。意味は、スローログを有効にし、1秒を超えたクエリを記録し、ログを指定ファイルに書き出すというものです。1日動かした後、mysqldumpslowやpt-query-digestでログをクエリ種別ごとに集計し、最も遅いSQLのTOP 10を洗い出します。

ステップ 操作 ツール・コマンド 目的
1. スローログを有効化 my.cnfに3行の設定を追加 MySQLを再起動して反映 スローSQLを記録
2. 1日動かす 通常どおりサイトに1日アクセス —— 実際のスロークエリを収集
3. 集計・分析 SQLフィンガープリントで集計 mysqldumpslow -s t TOP 10を特定
4. 1件ずつEXPLAIN 実行計画を確認 EXPLAIN SQL文 インデックスを使っているか判断
5. 最適化後に再計測 最適化前後の所要時間を比較 SHOW PROFILEまたは計測 効果を確認

シナリオ例:商品絞り込みページが極端に遅いと気づき、スローログを見るとTOP 1のスロークエリはmeta_queryを含むSQLで、wp_postmetaテーブルの特定のカスタムフィールドを検索していました。EXPLAINで分析すると、type列はALL(全表スキャン)、rows列は12万行のスキャンを示しています。このSQLは毎回wp_postmetaテーブルを全件走査しているのです。この時点で、サーバーをアップグレードするのではなく、meta_keyフィールドにインデックスを追加すべきだと分かります。

スロークエリログは常時有効にしないでください。それ自体がIO負荷を伴います。1日動かしてデータを収集したら無効にするか、閾値を2秒に上げて最も遅いものだけを記録しましょう。日常的な監視にはデータベース性能監視ツール(Prometheus+Grafanaなど)を使います。詳細はサイト監視とアラートのチェックリスト。

三、インデックス最適化:どのフィールドにインデックスを追加すべきか

EXPLAINの結果で最も重要な2列はtypeとrowsです。type列がALLなら全表スキャンなので、インデックスの追加が必須です。refやeq_refならすでにインデックスを使っているので、手を加える必要はありません。rows列はMySQLが何行をスキャンすると見積もっているかを示し、数字が大きいほど危険です。1万行以内なら許容範囲ですが、10万行以上なら最適化すべきです。

WordPressデータベースで最もインデックスを追加すべきなのはwp_postmetaテーブルです。このテーブルはデフォルトでpost_idのインデックスしかありませんが、実際のクエリではmeta_keyやmeta_valueで絞り込むことがよくあります。(meta_key, meta_value)の複合インデックスを追加すると、カスタムフィールドで絞り込むクエリが10倍以上速くなります。注意すべきは複合インデックスであり、meta_key単体にインデックスを追加するのではないことです。meta_key単体では、クエリにmeta_valueの絞り込みが含まれる場合にテーブルへ戻る必要があります。

テーブル クエリの場面 推奨インデックス 効果
wp_postmeta meta_key+meta_valueで絞り込み (meta_key, meta_value(191)) 10倍以上高速化
wp_postmeta post_idでmetaを検索 post_idインデックスが既存 変更不要
wp_options option_nameで設定を検索 option_nameインデックスあり 変更不要
wp_comments post_idでコメントを検索 comment_post_IDインデックスあり 変更不要
wp_posts post_type+post_statusで検索 複合インデックスあり 変更不要
プラグインが独自に作成したテーブル 日付/ステータスで絞り込み EXPLAINを見て判断 必要に応じて追加

直感に反する判断:遅いクエリを見るとすぐインデックスを追加したくなりますが、インデックスは無料ではありません。各インデックスはディスク容量を消費し、INSERTやUPDATEのたびにMySQLはすべてのインデックスを更新します。wp_optionsのような書き込みが多く読み取りが少ないテーブル(管理画面で設定を保存するたびに書き込みが発生)では、やたらとインデックスを追加するとかえって書き込みが遅くなります。正しいやり方は、読み取りが多く書き込みが少ないテーブル(wp_postmeta、wp_posts)には積極的にインデックスを追加し、書き込みが多く読み取りが少ないテーブル(wp_options、wp_comments)には慎重に、追加せずに済むなら追加しないことです。

インデックスを追加する前に必ずstaging環境でテストしてください。本番DBで直接ALTER TABLEを実行すると、大きなテーブルでは数分間ロックされ、サイトが502になる可能性があります。本番環境でインデックスを追加する際は、pt-online-schema-changeやgh-ostなどのオンラインDDLツールを使えばロックされません。craterの海外向けサイトDBでの保守経験はこちらcraterの技術スタックと4つの自社開発プラグイン。

四、テーブル構造の最適化:wp_postmetaとwp_optionsの落とし穴

インデックスの追加では解決できない問題もあり、テーブル構造を変える必要があります。wp_postmetaのキーバリュー設計はまさにこの類の問題です。すべてのカスタムフィールドを1つのテーブルに保存するため、1つの商品の仕様パラメータを調べるのに何度もJOINが必要になります。商品数が数千、カスタムフィールドが数十になると、このテーブルは数十万行に膨れ上がり、インデックス最適化の効果は頭打ちになります。

ここで2つの方向性があります。1つはカスタムテーブルでpostmetaを置き換え、頻繁にクエリされるカスタムフィールドを別テーブルに分けてフィールド型ごとに保存し、クエリ時に対応するテーブルを直接参照する方法。もう1つはWordPressのwp_options autoload機能を使い、読み取りが多く書き込みが少ない設定項目を自動ロードに設定して、毎回optionsテーブルをクエリするのを避ける方法です。B2B独立サイトの場合、方向性1は商品パラメータが複雑なサイトに、方向性2はプラグインを多く導入しているサイトに適しています。

問題 症状 改善の方向 リスク
wp_postmetaの肥大化 絞り込みページが遅い、テーブルが10万行超 頻繁に使うフィールドをカスタムテーブルに コード変更、テストが必要
wp_optionsの自動ロードが多すぎる 管理画面が遅い、autoloadが1MB超 不要なプラグイン設定を削除 削除前にバックアップ
wp_postsの下書き/リビジョンが多すぎる 記事一覧が遅い、不要なリビジョンが蓄積 自動リビジョンと下書きを削除 削除前に確認
wp_commentmetaの肥大化 コメント一覧が遅い スパムコメントの自動保存を停止 影響は小さい
wp_usersテーブルが大きい ユーザー管理が遅い 非アクティブユーザーを削除 誤削除に注意

例:ある産業機器メーカーの公式サイトに50の製品があり、各製品に20のカスタムパラメータ(材質、出力、サイズ、精度など)があるとします。これらのパラメータがすべてwp_postmetaに保存されていると、製品詳細ページを1回表示するだけでwp_postmetaを20回JOINすることになります。正しい方法は、wp_product_specsというカスタムテーブルを作り、20のパラメータを列として1行にまとめることです。製品パラメータの取得はSELECT1回で済み、JOINは不要になります。開発工数はそれほど増えませんが、性能の向上は桁違いです。

テーブル構造を変更する前に必ずデータベースをバックアップしてください。バックアップと復元の手順はサイトのバックアップと復元演習をご覧ください。データベースがすでに攻撃を受けたことがある場合のクリーンアップと強化の手順はサイトがハッキングされた後の対処。

5. 最適化後の検証と保守

インデックスを追加したりテーブル構造を変更したら、それで終わりではありません。必ず次の3つを行います。1つ目は、EXPLAINで最適化前のスロークエリを再実行し、type列がALLからrefまたはeq_refに変わったことを確認します。2つ目は、最適化前後の実際の処理時間を比較し、スロークエリが本当に速くなったことを確認します。3つ目は、1週間観察してスロークエリログに新しいスロークエリが現れないか確認します。最適化はボトルネックを別の場所に移すことがよくあるからです。

検証項目 検証方法 合格基準 不合格時の対応
EXPLAIN実行計画 スロークエリをEXPLAINで再確認 typeがALLでなくなり、rowsが1桁減る インデックスが効いていないので、フィールド型を確認
実際のクエリ処理時間 SQLを直接計測 1秒以上から100ミリ秒以内に短縮 インデックスが適切でないので再分析
スロークエリログ 3日間実行してログを確認 元のスロークエリが消えるか大幅に減少 新しいスロークエリがあれば最適化を続行
書き込み性能 INSERT/UPDATEの処理時間を計測 書き込みが目立って遅くなっていない インデックスを追加しすぎたので冗長なインデックスを削除
フロントページの表示時間 Lighthouseのスコア キャッシュミス時のLCP<1.5秒 他にもボトルネックがある

データベースの最適化は一度きりの作業ではありません。コンテンツの増加、プラグインの更新、テーマの改修によって新しいスロークエリが発生します。月に一度はスロークエリログを分析し、TOP 10に変化がないか確認することをおすすめします。日々の保守においてデータベースの最適化は定番項目です。詳しくは公式サイト保守で毎月やる6つのことをご覧ください。独立系サイトの技術スタック全体の選定フレームワークは環形山テックスタック、PHPバージョンのアップグレードとデータベース設定の連携は公式サイト保守ガイド。

FAQ よくある質問

WordPressでMySQLのスロークエリログを有効にするには?
MySQLの設定ファイルmy.cnf(Linux)またはmy.ini(Windows)に3行追加します。slow_query_log=1でスローログを有効にし、long_query_time=1でしきい値を1秒に設定し、slow_query_log_file=/var/log/mysql/slow.logでログのパスを指定します。変更後はMySQLを再起動します。1日実行したらmysqldumpslow -s tで時間順に集計し、最も遅いSQLのTOP 10を特定します。長期間有効にしたままにせず、データを収集したら無効にするか、しきい値を2秒に上げてください。
wp_postmetaテーブルはなぜこんなに遅いのか?
これはキー・バリュー型の設計だからです。カスタムフィールドはすべて1つのテーブルに格納され、1フィールドにつき1行になります。1つの商品の20個のパラメータを取得するには、このテーブルを20回JOINする必要があります。テーブルが数十万行に膨らむと、meta_queryを含むちょっとした絞り込みでも全表走査になります。改善策は(meta_key, meta_value)の複合インデックスを追加することです。クエリは10倍以上速くなります。商品パラメータが複雑な場合は、カスタムテーブルを作って頻繁に使うフィールドを別に保存するのが最善です。
インデックスを追加するとサイトが遅くなりますか?
なります。インデックスはそれぞれディスク容量を消費し、INSERTやUPDATEのたびにMySQLがすべてのインデックスを同時に更新します。読み取りが多く書き込みが少ないテーブル(wp_postmeta、wp_posts)では、インデックスの効果がコストをはるかに上回ります。逆に書き込みが多く読み取りが少ないテーブル(wp_options、wp_comments)では、やたらにインデックスを追加すると書き込みが遅くなります。原則は、EXPLAINで全表走査を確認してから追加することです。感覚で追加してはいけません。追加後は書き込み性能を測定してください。
EXPLAINの結果はどう見ればいいですか?
typeとrowsの2列を見ます。type列がALLなら全表走査なので、インデックスの追加が必須です。refやeq_refならすでにインデックスが使われています。rows列はMySQLが推定する走査行数で、1万行以内なら問題ありませんが、10万行以上は危険です。またkey列で実際にどのインデックスが使われたかを確認します。keyがNULLならどのインデックスも使われていません。
本番DBでインデックスを追加するとテーブルがロックされますか?
されます。大きなテーブルで直接ALTER TABLE ADD INDEXを実行すると数分間ロックされ、サイトは502になります。本番環境でインデックスを追加するときは、pt-online-schema-changeやgh-ostなどのオンラインDDLツールを使えばロックされません。小さなテーブル(1万行以内)なら直接追加しても問題ありません。追加前には必ずバックアップを取り、追加後はEXPLAINで実行計画を検証してください。
データベースの最適化はどのくらいの頻度でやるべきですか?
月に1回、スロークエリログを分析してTOP 10に変化がないか確認することをおすすめします。コンテンツの増加、プラグインの更新、テーマの変更は、いずれも新しいスロークエリを生みます。日々の監視では、スロークエリ数、DB接続数、キャッシュヒット率をダッシュボードにして、アラートのしきい値を設定しましょう。特定のページが急に遅くなったら、まずスローログを見るべきで、サーバーのアップグレードではありません。

最後に

データベース最適化で最も陥りやすい遠回りは、遅くなるとすぐサーバーをアップグレードすることです。1コア2GBから4コア8GBに上げて倍の費用を払っても、スロークエリは遅いままです。ボトルネックはCPUではなく、インデックスが使われていない全表走査だからです。正しい順序は常に、まずスローログを有効にしてどのSQLが遅いかを特定し、次にEXPLAINで実行計画を確認し、最後にインデックスを追加するかテーブル構造を変えるかを決めることです。

3ステップで解決します。まずスロークエリログを1日回して、mysqldumpslowでTOP 10を洗い出します。次に1件ずつEXPLAINして、type=ALLのものにインデックスを追加。wp_postmetaは(meta_key,meta_value)の複合インデックスを優先します。最後に最適化後の再テストで、遅いSQLが1秒から100ミリ秒以内に収まったことを確認します。この3ステップを終えると、サーバー設定は一切変えていないのに、ページ表示速度が数倍速くなっているはずです。

覚えておきたい一言。データベースが遅いときは、まずスロークエリログを確認しましょう。サーバーのスペックを上げるのは後回しです。遅いクエリの80%はインデックスが効かず全表走査になっているだけで、インデックスを1つ正しく追加するほうが、プランを1段階上げるより効果があります。

环形山
环形山 クレーター専門家

公式アカウント · 環形山 Crater

環形山(Crater)は、WordPress独立サイト開発+Google SEO+GEO全域成長に特化した海外展開支援会社で、スマートハードウェア、産業機器、SaaS、新エネルギーなどの分野を手がけています。

プロダクト能力方法論ホワイトペーパー業界レポート
965件のシェア
78件のナレッジベース