【入門編】wp_postmetaのシリアライズされたデータに対するMySQLの検索負荷とJSON型への移行検討 – WordPress 内部コア・データベース構造とパフォーマンス最適化解析バイブル

こんにちは!WordPressの裏側の仕組みやデータベース構造に興味を持ってこのページを開いてくれたということは、君ももう、ただの「使い方を覚えるだけのユーザー」から「システムを意のままに操るエンジニア」への階段を登り始めている証拠ですね。素晴らしい着眼点です。

今回は、WordPress開発の現場で避けて通れない「`wp_postmeta`のシリアライズ問題と、MySQLのJSON型によるパフォーマンス最適化」について、コアの内部構造まで踏み込んで徹底的に解説していきますね。

他の言語からWordPressに入ってきた開発者なら誰もが一度は頭を抱える「あの仕様」の正体と、それを美しく解決するモダンなアプローチを一緒にマスターしていきましょう!

—

1. なぜ `wp_postmeta` のシリアライズデータは検索の敵なのか?

まずは、WordPressの心臓部であるデータベース構造のおさらいから始めましょう。
投稿のカスタムフィールド(メタデータ)を保存する `wp_postmeta` テーブルは、基本的に次のようなシンプルな構造をしています。

+————+———+—————-+——————–+
| meta_id | post_id | meta_key | meta_value |
+————+———+—————-+——————–+
| 101 | 45 | product_specs | a:3:{s:5:…} |
+————+———+—————-+——————–+

ここで注目してほしいのが `meta_value` カラムです。WordPressでは、配列やオブジェクトデータをそのままデータベースに突っ込むために、PHPの `serialize()` 関数を使って文字列に変換(シリアライズ)して保存します。

例えば、商品のスペック情報を配列で保持しようとすると、`meta_value` の中身はこんなカオスな文字列になります。

a:3:{s:5:”color”;s:3:”red”;s:4:”size”;s:2:”Lතික”;s:5:”price”;i:12000;}

ここで何が起きるか?(最大のボトルネック)

もし君が、「価格(`price`)が10000円以上の商品を `wp_postmeta` から検索したい!」と思ったとき、SQLはどう書くでしょうか?

— 悪夢のLIKE検索
SELECT FROM wp_postmeta
WHERE meta_key = ‘product_specs’
AND meta_value LIKE ‘%price”;i:1200%;%’;

…お気づきでしょうか? シリアライズされたデータは「ただの文字列」です。そのため、MySQLは内部の構造を理解できず、インデックスを完全に無視してテーブル全体を上から下まで舐め回す「フルテーブルスキャン(全表検索)」を実行します。

データ量が数万件を超えたあたりからCPU使用率が跳ね上がり、サイト全体が重くなる……これが、シリアライズデータがパフォーマンスの killer(キラー)と呼ばれる所以(ゆえん)なんですよね。ここをクリアできれば、君のWordPress開発スキルは一段と洗練されますよ!

—

2. 救世主降臨:MySQL 5.7以降の「JSON型」という選択肢

「じゃあ、どうすればいいの?」という話ですよね。
MySQL 5.7(およびWordPressが現在サポートしているすべてのモダンなバージョン)以降には、ネイティブな JSON型(JSONデータタイプ) が備わっています。

PHPのシリアライズの代わりに、データをJSON形式で保存し、MySQLのJSON関数を使ってクエリを投げれば、データベースが自らデータの構造を解釈し、インデックスを活用した超高速な検索が可能になります。

イメージ図:シリアライズ vs JSON型

  • 従来のシリアライズ(検索不能・インデックス不可)

`meta_value` = `a:2:{s:4:”city”;s:5:”Tokyo”;s:3:”age”;i:25;}`
(MySQLからは、ただの長い文字列に見えている)

  • MySQL JSON型(構造化・インデックス可能)

`meta_value` = `{“city”: “Tokyo”, “age”: 25}`
(MySQLがキーとバリューを理解し、内部で最適化する)

—

3. 実践:カスタムフィールドをJSONとして保存・検索するコード

ここからは、実際にWordPressのコードベースでどのようにJSON型を扱い、パフォーマンスを担保するかをみていきましょう。

今回は、カスタム投稿タイプ「product」に対して、JSON形式でスペックデータを保存するカスタムメタボックスの処理を想定します。

① データの保存(PHP側)

WordPressの `update_post_meta()` を使う際、配列をシリアライズせずに `json_encode()` でJSON文字列にして保存します。

/

  • 商品スペックをJSON形式で安全に保存する
  • @param int $post_id 投稿ID
  • @param array $specs 保存したいスペックデータの連想配列

/
function my_save_product_specs( int $post_id, array $specs ): void {
// セキュリティのためのnonceチェックなどは省略せずに行ってくださいね

// PHPの配列をJSON文字列に変換
$json_value = json_encode( $specs, JSON_UNESCAPED_UNICODE );

if ( false === $json_value ) {
// エンコード失敗時のエラーハンドリング
return;
}

// wp_postmetaに保存(キー名を ‘product_specs_json’ とする)
update_post_meta( $post_id, ‘product_specs_json’, $json_value );
}

// 実行例
$sample_specs = [
‘color’ => ‘blue’,
‘size’ => ‘M’,
‘price’ => 15000,
];
// my_save_product_specs( 123, $sample_specs );

② JSONデータを活用した高速検索(SQL・カスタムWP_Query)

次に、保存したJSONデータから「価格が10000円以上の商品」を効率よく取得するSQLの書き方です。MySQLの `JSON_EXTRACT`関数(またはアロー演算子 `->>`)を使用します。

— MySQL 5.7+ で使えるJSON検索クエリ
SELECT p.ID, p.post_title, m.meta_value
FROM wp_posts AS p
INNER JOIN wp_postmeta AS m ON p.ID = m.post_id
WHERE m.meta_key = ‘product_specs_json’
— JSONの ‘price’ キーの値を取り出し、数値として比較する
AND CAST(m.meta_value ->> ‘$.price’ AS UNSIGNED) >= 10000;

このクエリの素晴らしいところは、MySQLの「生成的列(Generated Columns)」や「マルチバリューインデックス」を組み合わせることで、JSONの特定キーに対してB-Treeインデックスを張れる点です。LIKE検索とは比較にならないほどの爆速レスポンスを実現できます。

—

4. 陥りがちな文法エラーと注意点

初心者の開発者がこの領域に踏み込んだとき、よくハマる罠がいくつかあります。事前に知っておけば怖くありません!

罠1: ダブルクォーテーションのクォート忘れ

SQL内でJSONのキーを指定する際、シングルクォーテーションとダブルクォーテーションの入れ子を間違えると、MySQL側でSyntax Error(構文エラー)になります。

  • NG: `m.meta_value ->> ‘$.[price]’`
  • OK: `m.meta_value ->> ‘$.price’`

罠2: データ型の不一致(暗黙の型変換コスト)

JSONから取り出したデータは、デフォルトでは文字列(String)として扱われます。もし数値比較や大小比較(`>`, `<`)を行う場合、先ほどのSQL例のように `CAST(... AS UNSIGNED)` や `CAST(... AS DECIMAL)` で明示的に型をキャストしてあげないと、意図しないソート順や比較ミスが起きるので注意してくださいね。

罠3: WordPress標準関数(get_post_meta)との付き合い方

WordPressのコア関数 `get_post_meta( $post_id, ‘product_specs_json’, true )` は、値がJSON文字列であっても、ただの文字列として返してくれます。
そのため、PHP側で扱うときは必ず `json_decode( $value, true )` を通して連想配列に戻す一手間を忘れないようにしましょう。

—

先輩エンジニアからの温かいエール

お疲れ様でした!今回は少しディープなデータベースの内部構造とパフォーマンス最適化のお話をしました。

「シリアライズデータは便利だけど、検索には向かない。検索性やスケーラビリティを求めるならJSON型や専用カラムを検討する」——この判断ができるようになるだけで、君が作るWordPressサイトは、数万・数百万アクセスの高負荷に耐えうる「プロフェッショナルなシステム」へと生まれ変わります。

最初は難しく感じるかもしれませんが、実際にローカル環境でデータベースを覗きながら試してみると、驚くほどスッキリ理解できるようになりますよ。
一つひとつ確実にモノにして、最強のフルスタックエンジニアを目指していきましょう!君なら絶対にバッチリマスターできますよ!

タイトルとURLをコピーしました