WordPressデータベース統合の深淵:ID衝突を回避し、整合性を担保する「マイグレーションの極意」
WordPressのデータベースは、一見するとシンプルだ。しかし、複数のインスタンスを統合しようとした瞬間、その設計の「脆弱性」が牙を剥く。特に `wp_posts.ID` のプライマリキー(AUTO_INCREMENT)は、単一インスタンス環境を前提とした設計であり、複数サイトをマージする際には最大のボトルネックとなる。
本稿では、WordPressの内部構造を熟知したエンジニアが、「ID衝突を回避し、メタデータの整合性を1bitの狂いもなく維持する」ためのデータ移行戦略を伝授する。
—
なぜ「単純なSQL移行」は破綻するのか
多くの開発者が陥る罠は、`mysqldump` でエクスポートしたテーブルをそのままターゲットへ流し込むことだ。これには以下の3つの「死」が待っている。
1. ID衝突: ターゲットDBの `AUTO_INCREMENT` カウンターが古いIDを保持している場合、新たなINSERTが既存データと衝突する。
2. メタデータの孤立: `wp_postmeta` の `post_id` は `wp_posts` のIDを指している。IDをずらしてインポートした場合、リレーションが崩壊し、データが「幽霊化」する。
3. タクソノミーの不整合: `wp_term_relationships` の `object_id` もまた、移行元のIDに依存している。
これらを解決するための要諦は、「移行元の古いIDをキーとして維持し、マッピングテーブルを生成した上で、関係性を再構築する」ことにある。
—
堅牢な移行のための設計パターン
移行を行う際は、DB直接操作(Raw SQL)とWordPressの `wp_insert_post()` を使い分ける必要がある。パフォーマンスを最優先するなら直接SQLを叩くべきだが、フック(`save_post` 等)の副作用を無視できない場合はAPIを介すべきだ。
今回は、「整合性を最優先した直接SQL移行」の設計パターンを示す。
ステップ1:IDマッピングの定義
まずは移行元IDと移行先IDを紐付けるテーブルを一時的に作成する。
— 移行元のIDと移行先のIDを保持するマップテーブル
CREATE TABLE wp_migration_id_map (
old_id BIGINT UNSIGNED NOT NULL,
new_id BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (old_id)
);
ステップ2:整合性を保つデータ移行のプロダクションコード(PHP/WP-CLI実装)
以下は、WP-CLIコマンドとして実装可能な、整合性を担保した移行ロジックの断片だ。
/
- 移行ロジック:IDの整合性を担保しながらレコードを転送する
/
function migrate_posts_with_integrity($old_db_connection) {
global $wpdb;
// 1. 移行元からデータを取得
$old_posts = $old_db_connection->get_results(“SELECT FROM wp_posts”);
foreach ($old_posts as $post) {
// 2. 新規IDを強制割り当てずにINSERT
// post_idを保持したままインサートすることでメタデータとの整合性を確保する
$inserted = $wpdb->insert($wpdb->posts, [
‘ID’ => $post->ID, // ここで元のIDを強制的に割り当てる(AUTO_INCREMENTを一時的に回避)
‘post_title’ => $post->post_title,
‘post_content’ => $post->post_content,
‘post_status’ => ‘publish’,
// … 他のフィールド
]);
if ($inserted) {
// 3. メタデータの移行:post_idをキーとしてそのまま紐付け
migrate_post_meta($post->ID, $old_db_connection);
}
}
}
/
- 関連するメタデータの移行
/
function migrate_post_meta($post_id, $old_db_connection) {
global $wpdb;
$metas = $old_db_connection->get_results(“SELECT FROM wp_postmeta WHERE post_id = $post_id”);
foreach ($metas as $meta) {
$wpdb->insert($wpdb->postmeta, [
‘post_id’ => $meta->post_id,
‘meta_key’ => $meta->meta_key,
‘meta_value’ => $meta->meta_value,
]);
}
}
—
パフォーマンスと保守性のための注意点
1. `AUTO_INCREMENT` のオフセット管理
移行後に `wp_posts` の `AUTO_INCREMENT` カウンターが最大IDより小さくなっていると、後の記事投稿でエラーが発生する。移行完了後、必ず以下のSQLを実行せよ。
— 現在の最大IDを確認し、インクリメント値を再設定する
SET @max_id = (SELECT MAX(ID) FROM wp_posts);
SET @query = CONCAT(‘ALTER TABLE wp_posts AUTO_INCREMENT = ‘, @max_id + 1);
PREPARE stmt FROM @query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
2. トランザクションとデッドロック
数万件を超える移行を行う場合、`$wpdb->query(‘START TRANSACTION’)` を使用し、バッチ単位でコミットせよ。単一のトランザクションに全データを詰め込むと、MySQLのUndoログが枯渇し、パフォーマンスが劇的に低下する。
3. Object Cacheの汚染
移行中、WordPressの `wp_cache_` (Object Cache) が古いデータを保持している可能性がある。バッチ処理の最後には必ず `wp_cache_flush()` を実行し、整合性を強制的にリセットすることが、後の「なぜか投稿が表示されない」というバグを防ぐ唯一の道だ。
—
最後に:エンジニアとしての矜持
WordPressのデータ移行は、単なる「データの引っ越し」ではない。それはシステムの基盤となるRDBの構造を理解し、その制約をハックする高度な知的作業だ。
コードを書く際は常に「この操作が失敗したとき、どのデータが孤立するのか?」を自問自答してほしい。美しい設計とは、成功の快感ではなく、失敗したときにシステムを破壊しない「退避路」が用意されている設計のことだ。
君たちが手がける大規模なWordPress統合プロジェクトが、この知見によってより堅牢なものになることを期待している。健闘を祈る。