こんにちは! WordPressの内部構造やデータベースの仕組みに興味を持ってくれて嬉しいです。他のプログラミング言語やフレームワークを経験した人ほど、「WordPressのデータベースって、どうしてこうなっているんだろう?」と疑問に思うことが多いですよね。
今回は、WordPressの心臓部である「データベース(MySQL/InnoDB)」の物理構造と、そのパフォーマンスを限界まで引き出すためのInnoDBバッファプールの最適化について、少しディープに、でも分かりやすく解説していきますね。
ここをクリアすれば、WordPressのパフォーマンスチューニングの本質がぐっと見えてきますよ。一緒にバッチリマスターしていきましょう!
—
1. WordPressのデータベースと「InnoDBページ」の基本構造
WordPressのデータは、お馴染みのMySQL(またはMariaDB)に保存されています。中でも記事データを担当する `wp_posts` テーブルや、そのメタ情報を管理する `wp_postmeta` テーブルは、日々のサイト運用で最も頻繁にアクセスされる場所です。
ここで、データベースのストレージエンジンである InnoDB がデータをどのようにディスク上で扱っているか、その最小単位を知る必要があります。
ページ(Page)という単位を知ろう
InnoDBでは、データをディスクから読み書きする際、1つずつバラバラに処理するのではなく、「ページ(Page)」という決まった大きさ(デフォルトでは 16KB)のブロック単位でまとめて管理しています。
イメージとしては、こんな感じです。
[ 16KBのInnoDBページ ]
├── ページヘッダ(メタ情報)
├── 行データ1 (wp_posts の 1行分)
├── 行データ2 (wp_posts の 1行分)
└── … (16KBの枠に収まる分だけ詰め込まれる)
データベースへの問い合わせ(クエリ)が発生すると、MySQLは必要なデータが含まれる「16KBのページ丸ごと」を、メモリ(RAM)上に確保された領域である「InnoDBバッファプール」に読み込みます。
—
2. なぜ `wp_posts` の「行サイズ」が問題になるのか?
ここで、今日の核心である「行サイズとページング効率」の話に入りましょう。
WordPressで記事を書くとき、`wp_posts` テーブルの `post_content` カラムに文章やHTML、さらにはGutenberg(ブロックエディター)の膨大なJSONデータが保存されますよね。
この `post_content` はデータ型に `LONGTEXT` が使われており、1つのセルだけで最大4GBのデータを保存できます。
オーバーフローページ(Off-pageストレージ)の罠
「じゃあ、いくら記事が長くても大丈夫だよね?」と思いますよね。ここが落とし穴なんです。
InnoDBでは、1つのページ(16KB)の中に、行のデータが入り切らない場合、または非常に大きなカラム(VARCHARやTEXTなど)がある場合、その大きなデータ本体をメインのページから切り離し、「オフページ(オーバーフローページ)」という別の場所に保存します。
メインのページには、「実際のデータはこっちのページにあるよ」という20バイト程度のポインタ(アドレス)だけが残されます。
[ メインの16KBページ ]
├── 行ID: 101
├── post_title: “こんにちは”
└── post_content のポインタ ───> [ 別のオーバーフローページ(16KB) ]
└── 巨大な記事本文データ…
何がパフォーマンスを落とすのか?
1つの記事の行サイズが大きくなりすぎたり、巨大な `post_content` を持つ行が1つの16KBページ内にギュウギュウに詰め込まれたりすると、「1つのページに入る行数が極端に減る」という現象が起きます。
例えば、通常なら1ページに50行入るはずのデータが、1行あたりのサイズが大きいせいで、1ページに数行しか入らなくなるとどうなるでしょうか?
1. バッファプールの圧迫:
一覧表示(`WP_Query` による複数記事の取得など)を行う際、MySQLは本来より何倍もの数の「ページ」をディスクからメモリ(バッファプール)に読み込まなければならなくなります。
2. キャッシュヒット率の低下:
バッファプールの容量には限りがあります。無駄に大きなページやオーバーフローページでメモリが埋まってしまうと、本当に必要なキャッシュが追い出され(キャッシュミス)、ディスクI/O(読み込み遅延)が多発してサイト全体が重くなります。
—
3. 実践:現在のテーブルと行の状態をコードで確認してみよう
では、実際にあなたのWordPressサイトのデータベースがどうなっているか、開発環境やステージング環境で確認してみましょう。
以下のSQLを実行すると、各テーブルのサイズや平均行長を調べることができます。
— 各テーブルのデータサイズと平均行長(Average Row Length)を確認するクエリ
SELECT
table_name AS `テーブル名`,
table_rows AS `概算行数`,
ROUND(data_length / 1024 / 1024, 2) AS `データ容量(MB)`,
ROUND(index_length / 1024 / 1024, 2) AS `インデックス容量(MB)`,
avg_row_length AS `平均行長(バイト)`
FROM
information_schema.tables
WHERE
table_schema = ‘あなたのデータベース名’
AND table_name LIKE ‘wp_%’;
💡 コードの意味とポイント
- `avg_row_length`(平均行長)の値が大きすぎる場合(例えば数KB〜数十KB以上)、記事の本文がその行に直接含まれていたり、オーバーフローページを多用しているサインです。
- この数値が大きいテーブルが頻繁にフルスキャン(全件検索)されると、InnoDBバッファプールが一瞬で枯渇します。
—
4. 陥りがちな文法・設計エラーと対策
WordPress開発の現場で、パフォーマンスを意識せずにやってしまいがちな失敗例とその対策を見ていきましょう。
❌ やってしまいがちな失敗
1. `WP_Query` やカスタムSQLで不要な `post_content` まで丸ごと取得する
- 記事一覧ページを作るときに、つい `posts_per_page => 10` として全てのカラム( `post_content` を含む)を取得してしまう。
- 結果: 使わない巨大なテキストデータまでメモリ上にロードされ、バッファプールを無駄遣いします。
2. カスタムフィールド(`wp_postmeta`)に巨大な配列やシリアライズデータを詰め込む
- 1つのメタキーに対して、何MBもあるJSONやオブジェクトを保存してしまう。
⭕ 正しい対策アプローチ
もし「記事一覧で本文は使わず、タイトルとサムネイルだけでいい」という場合は、`fields` 引数やSQLの取得カラムを制限して、メモリへの負荷を最小限に抑えましょう。
例えば、WordPressの `WP_Query` でパフォーマンスを最適化するコード例です:
// パフォーマンスを意識した WP_Query の例
$args = array(
‘post_type’ => ‘post’,
‘posts_per_page’ => 10,
// 【重要】必要なフィールドだけに絞ることで、メモリ消費とデータベースの負荷を軽減
‘fields’ => ‘ids’,
);
$post_ids_query = new WP_Query( $args );
if ( $post_ids_query->have_posts() ) {
// IDだけを先に取得し、必要なデータだけを効率よくキャッシュ・取得する
$post_ids = $post_ids_query->posts;
// 必要に応じてオブジェクトを一括キャッシュから取得するなどの処理へ繋げる
// update_post_cache( $post_ids );
}
さらに、データベースサーバー側(`my.cnf` / `my.ini`)の InnoDBバッファプールサイズ(`innodb_buffer_pool_size`) は、一般的に利用可能なRAMの50%〜80%に設定し、メモリ上でデータが完結するようにチューニングするのが鉄則です。
—
まとめ
今回は、`wp_posts` テーブルの行サイズとInnoDBのページング効率、そしてバッファプールの関係について物理的な視点から解説しました。
- InnoDBは16KBの「ページ」単位でデータをメモリに読み込んでいる。
- `post_content` が肥大化すると、行サイズが大きくなり、1ページあたりの格納効率が落ちる。
- 結果としてオーバーフローページが増え、InnoDBバッファプールのヒット率が下がり、サイト全体のパフォーマンスが低下する。
- クエリを書く際は、不要な巨大カラム(`post_content` など)を読み込まない工夫がプロの技。
仕組みを深く理解しているエンジニアと、ただコードを書くだけのエンジニアの差は、こうした「データベースの裏側がどう動いているか」を想像できるかどうかにあります。
ここをクリアしたあなたなら、もうワンランク上のWordPressエンジニアですよ。ぜひ実際の開発現場でも意識してみてくださいね!