第5回: DB事故と移転。シリアライズデータと整合性の防衛
DB の移転で一番怖いのは「動いてはいるけど、どこかが壊れている」状態です。シリアライズの仕組みを理解しておけば、その壊れ方を未然に防げます。
📌 この記事の要点
- 検索置換でサイトが壊れる最大の原因は「シリアライズデータ」の扱い
- wp search-replace を使った高速かつ安全なドメイン・パス置換の手順
- 文字化け(utf8mb4_unicode_ci と general_ci の不整合)とキー破損の復旧フロー
判断表:DB事故を防ぐ作業分岐
| 状況 | 主なリスク | 先に取る判断 | 推奨手段 |
|---|---|---|---|
| ドメイン・パス移転 | シリアライズデータ破損、URL残存 | DB事故化する前に全量バックアップを固定 | wp search-replace --dry-run後に本実行 |
| 巨大DBのインポート | タイムアウト、途中投入、照合順序混在 | ブラウザ投入を避け、CLIで分割確認 | mysql CLI、分割SQL、事前charset確認 |
| 管理画面に入れない | 復旧操作の上書き、証跡消失 | ログとDBを先に退避してから権限を復旧 | 緊急ユーザー作成、プラグイン無効化、URL確認 |
| 文字化け・検索不能 | utf8mb4不整合、比較条件の破綻 | 症状を再現し、対象テーブルを限定 | SHOW FULL COLUMNS、照合順序統一、再変換 |
| AUTO_INCREMENT崩れ | 重複キー、投稿ID衝突、関連データ孤立 | INSERTを止め、最大IDと次回値を照合 | ALTER TABLE ... AUTO_INCREMENT、孤立行チェック |
第4回では、Reactエラーやヘッドレス構成で「画面が動かない」原因を追いました。最後に残る問いはひとつです。その状態がDBに永続化された瞬間、何が起きるか。
DB移転でURLを置換したら管理画面が崩壊する。侵入・改ざんがDBに残り続ける。これが「戻れないDB事故」です。
第5回は「DB事故を戻す・防ぐ回」です。
導入:DB事故は「戻れない」を作る
だから先に戻れる状態を作る
今回のゴール(できるようになること)
- シリアライズを壊さずに移転・置換できる
- 巨大DBでもタイムアウトさせずにimportできる(分割/CLI)
- wp search-replace を事故らせない型で運用できる
- 文字化け/照合順序不整合を症状から診断し統一できる
- AUTO_INCREMENT/重複キーを原因から潰して復旧できる
- 管理画面に入れない時でも緊急ユーザーを安全に作れる
DB事故の分類(今回扱う領域)
- 移転(ドメイン/パス変更)
- 置換(search/replace、特にシリアライズ)
- 文字化け(charset/collation混在)
- キー崩壊(AUTO_INCREMENT/重複キー)
- ログイン不能(権限/URL/プラグイン/WAF/DB破損)
まず環境を確定する(ここで手順が分岐する)
以降の手順は
「推奨フロー(SSH/WP-CLIあり)」
↓
「代替フロー(SSH不可・phpMyAdmin中心)」
の順で書きます。
環境が不明な場合は、まず以下で確定してください。
- DB種別(MySQL/MariaDB)を確定
- SSH/WP-CLI可否を確定(推奨はCLI)
- マルチサイトか判定
- レンタル/ VPS など制約を把握
✅確認コマンド(SSH/WP-CLIがある場合)
目的:環境判定(DB種別/マルチサイト/DBサイズ)
前提:WP-CLI可(不可ならphpMyAdminで同等SQL)
実行:
wp db query "SELECT VERSION();"
wp db query "SHOW VARIABLES LIKE 'version_comment';" # MariaDB判定のヒント
wp db query "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name LIKE '%_blogs';"
wp db sizeBash確認:推奨フロー(CLI)/代替フロー(SSH不可)を選ぶ
戻し方:なし
実行:
✅SSHが無い場合の確認(phpMyAdmin 等)
- DB種別/バージョン:
phpMyAdminの「データベースサーバー」表示、または「変数」(version) を確認 - マルチサイト判定:
wp-config.php に MULTISITE 定数があるか、または wp_blogs テーブルが存在するかを確認(接頭辞は環境で異なる)
安全の最低要件:危険操作の前に「復元可能」を作る
本番DBの作業は、復元テストまでが作業です。最低要件を満たせないなら、作業を止める判断が正解になります。
- DBフルバックアップ(dump)を取得し、復元テスト(最低でもテーブル数/件数確認)を行う
- 可能ならホスティングのスナップショット(サーバー機能)も併用する
- 作業前後で「URL」「テーブル接頭辞」「対象環境(本番/検証)」を明記し、取り違えを防ぐ
作業を止める判断基準(やらない根拠)
- バックアップ取得または復元テストができない
- 本番/検証や接頭辞が識別できない
- 侵入疑いが強いのに隔離/調査せず作業する
DB移転で壊れる「本当の理由」
移転=参照整合性の再構築
DB移転を「SQLを持っていく作業」と捉えると事故ります。
実態は、参照整合性(参照が正しく繋がっている状態)の再構築です。WordPressはDBに「データ」だけでなく「設定」「状態」「構造」も保存します。
壊れやすい代表ポイント(どこが壊れるか)
wp_options:home/siteurl、ウィジェット設定、プラグイン設定、キャッシュキー、サイト状態フラグwp_postmeta:
ページビルダーやブロックの属性、ACFなどの構造化メタwp_posts:
本文中のURL、ブロックコメント内の属性、ショートコードの引数- マルチサイト:
wp_blogs/wp_sitemeta、サイトごとのURLとパスの二重管理
移転時の定義(今回の前提)
- 移転=「URL(ドメイン)」「ファイルパス」「環境差分(HTTPS/HTTP、サブディレクトリ、CDN)」を揃え、DB内の参照を壊さずに繋ぎ直す作業
- 置換=「繋ぎ直しの手段」。ただし無差別置換は「構造破壊」になり得る
このあと扱う「シリアライズ事故」は、参照整合性を直すつもりで、構造そのものを壊してしまう典型です。
シリアライズ事故:壊れ方→検出→安全策
シリアライズデータ=PHPのserialize()形式で配列/オブジェクトを文字列化したもの(文字列長が埋め込まれるため、置換で長さがズレると復元できなくなる)。
典型症状(現場での見え方)
- 管理画面のウィジェットが消える/配置がリセットされる
- プラグイン設定画面が真っ白、設定保存が効かない
- ログに
unserialize()系の警告が出る(PHP error log に残りやすい) - 移転後に「表示は出るが一部機能だけ死ぬ」=DBの設定値だけ壊れているサイン
壊れる理由(検索置換が危険な場面)
シリアライズ文字列は s:12:"example"; のように、s: の直後に文字列長が入ります。ここでドメインやパスを置換すると長さが変わり、復元時にズレて破損します。
禁止例(やると高確率で壊れる)
- phpMyAdminの「検索置換」や、SQLの
UPDATE ... SET col = REPLACE(col, ...)を、wp_options/wp_postmetaなどに無差別に当てる - 本番DBに対して、対象テーブル/列を絞らずに置換を走らせる
- マルチサイトで、
--network相当の扱いを理解せずにサイトURLだけを置換する
検出アプローチ(壊れている/壊れそうを先に掴む)
検出は「ログ」→「データ」→「アプリ挙動」の順で、コストの低いものから当てます。
1️⃣ まずログを確認します。(第1回の型を流用)
unserialize系を拾う ※環境によりログパスは異なるので、まず場所を確定する
grep -R "unserialize" -n /path/to/logs | headBash2️⃣ DB側:
wp_optionsの中でserializeっぽい値をざっくり抽出する(厳密な判定ではないことに留意)もしWP-CLIが使えるなら「読み取り専用」で安全に棚卸しできる
wp db query "SELECT option_name FROM wp_options WHERE option_value LIKE 'a:%' OR option_value LIKE 'O:%' OR option_value LIKE 's:%' LIMIT 50;"Bash3️⃣置換前の棚卸し:
置換対象がどのテーブル/列に多いか(dry-runで見るのが一番安全)
→ 次セクションで型として固定
推奨の安全策(結論)
置換が必要なら、原則 wp search-replace を使い、dry-run→本番→検証→必要なら即ロールバックの順で固定します(WP-CLIのsearch-replaceはPHPのシリアライズデータを扱える旨が公式に明記されています)。
dry-runとは?
実際の更新を行わず、変更件数と対象範囲だけを確認する実行処理。
ロールバック判断ライン(戻すか進めるか)
- dry-run時点で置換件数が想定より桁違いに多い(スコープ設計ミス)
- 実行後にログへunserialize系が出始めた/管理画面の設定が飛んだ
- マルチサイトで想定外のサイトまで影響している(network扱いの理解不足)
WP-CLIでの安全な search-replace(型)
ここは「型」を覚えるだけで事故率が落ちます。WP-CLIの wp search-replace は、DB内の文字列を置換しつつ、PHPシリアライズデータにも対応すると公式に明記されています。
DB内の文字列を置換しつつ、PHPシリアライズデータにも対応
実行前チェックリスト(事故の多くはここで防げる)
- 対象環境を明示:URL / テーブル接頭辞 / 本番 or 検証
- dump取得+復元テスト(最低:テーブル数と件数の照合)
- マルチサイト判定(trueなら
--networkを検討) - 置換対象の棚卸し(dry-runで件数・対象テーブルを把握)
- 除外設計:
guid(投稿の恒久識別子。原則置換しない)などを決める
目的→前提→実行→確認→戻し方
以下はそのまま使えるテンプレです。ドメイン移転例(old→new)。
目的
DB内の参照URLを新ドメインへ安全に置換し、シリアライズ破損を避ける
前提
SSH/WP-CLIが使える。バックアップ済み。作業対象のDB接頭辞を把握している
実行1️⃣ dry-run:まず件数と対象を見て、想定外が無いか確認
wp search-replace 'http://old.example' 'https://new.example'
--dry-run
--all-tables-with-prefix
--skip-columns=guidBash実行2️⃣ 本番:dry-runでOKだった場合のみ実行
wp search-replace 'http://old.example' 'https://new.example'
--all-tables-with-prefix
--skip-columns=guidBash実行3️⃣ マルチサイトの場合:ネットワーク全体に適用する(必要な時だけ)
wp search-replace 'http://old.example' 'https://new.example' --network --all-tables-with-prefix --skip-columns=guidBash確認:
置換直後に「ログ」「管理画面の主要画面」「代表ページの表示」「ログイン/保存系」を最短で確認します。
- エラーログにunserialize系が増えていない
- ログイン→プロフィール保存→ウィジェット画面→メディア一覧(崩壊しやすい箇所)
- リンク切れ(画像/JS/CSS)が増えていない(DevToolsのNetworkで 404 を見る)
戻し方:
異常が出たら、置換で直そうとせず「dumpで戻す」が最短です。
例:事前に取得した dump.sql.gz へ戻す(詳細は次セクション)
※作業対象が本番DBであることを再確認してから
gunzip -c dump.sql.gz | wp db import -Bash危険例 vs 安全策(最小の対比)
- –dry-run で棚卸し → OKなら本番
- 対象テーブルはWP接頭辞に限定(–all-tables-with-prefix)
- guidは原則除外(–skip-columns=guid)
巨大DBの搬送:タイムアウトを設計で潰す
phpMyAdminで巨大SQLを投げてタイムアウト、は「仕様」です。ギガバイト級はGUI前提を捨てて、dump→圧縮→転送→CLIインポートに寄せます。
失敗パターン(先に潰す)
- phpMyAdminのアップロード上限/実行時間に引っかかる
- ブラウザ経由で途中切断
→ 半端に入って「重複キー」や「文字化け」へ連鎖 - 巨大1ファイルで再開不能(どこで死んだか不明)
推奨フロー(SSH/WP-CLIあり)
目的:巨大DBをタイムアウトさせずに搬送/importする
前提:SSH/WP-CLI可。作業前dumpは別保管
実行:
wp db export dump.sql
gzip -9 dump.sqlBash(転送)scp/rsync 等で dump.sql.gz を移動
gunzip -c dump.sql.gz | wp db import -Bash確認:
wp db query "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE();"
wp db query "SELECT COUNT(*) FROM wp_posts;"Bash戻し方:before_dump.sql.gz をimport
分割・ストリーム・再開可能性(巨大向けの追加手当)
再開不能が怖いなら、分割して「途中からやり直せる」形にします。
目的:分割して再開可能にする
前提:dump.sql がある
実行:
split -b 200m dump.sql dump.part.
for f in dump.part.*; do wp db import "$f" || break; doneBash確認:失敗したパート以降だけ再実行できる
戻し方:事前dumpをimport
代替フロー(SSH不可・phpMyAdmin中心)
SSHが無い場合は「DBの持ち出し」自体が制約になります。現実解は2つ。
- ホスティング側の「バックアップ/復元」機能
(DB単体のエクスポートが可能ならそれを使う) - 移行専用ツールのDB搬送機能を使う
(ただし置換・シリアライズ破損リスクは別途評価)
いずれの場合も、import後に必ず「照合順序」「AUTO_INCREMENT」「ログイン」を点検して、次の事故を未然に止めます。
文字化け/照合順序(collation)事故の対応
charset=文字コード体系(例:utf8mb4)
collation=その文字コードでの比較/並び順ルール(例:utf8mb4_unicode_ci)
症状別診断例
⚠ 症状A — 画面の日本語が「????」や「é」になる
🩺 原因
接続の文字セットが不一致(クライアント / サーバー / テーブル)
🔧 対応手順
1️⃣ 文字コードを確認します
SHOW VARIABLES LIKE 'character_set%';
SQL2️⃣ wp-config.php の DB_CHARSET / DB_COLLATE を確認します
⚠ 症状B — SQLエラーに「Illegal mix of collations」が出る
🩺 原因
テーブル / カラムの collation が混在
🔧 対応手順
1️⃣ information_schema で混在箇所を特定する
information_schema とは? DB やテーブル等に関するメタデータを格納しているデータベース。情報の絞り込み(特定スキーマ・テーブルの抽出)ができます。
2️⃣ 影響範囲を絞って統一(無差別 CONVERT は禁止)
2️⃣ 影響範囲を絞って統一(無差別CONVERTは禁止)







