2026年9月12日

2026年9月12日

WordPressのデータベーステーブルの整合性を定期チェックするスクリプト

WordPress のデータベースの整合性は、「テーブルそのものが壊れていないか」と「WordPress のデータ同士の関係が崩れていないか」の2段階で確認できます。前者は wp db check(内部で mysqlcheck を実行)、後者は孤立したメタデータなどを数える SQL で調べられ、どちらも WP-CLI と cron で定期実行できます。この記事では、読み取り専用で安全に動くチェックスクリプトの例と、異常が見つかったときの判断の仕方、定期実行の設定までを説明します。

スクリプトは「見つけて知らせる」ところまでにとどめ、修復や削除は人が内容を確認してから行う、という方針で組み立てます。自動で削除まで行うと、プラグインが意図的に残しているデータまで消してしまうおそれがあるためです。

チェックする内容

テーブルの破損(ストレージの層)

サーバーの強制終了やディスクの障害のあと、テーブルが読めなくなることがあります。MySQL/MariaDB の CHECK TABLE、またはそれを一括で行う mysqlcheck で確認できます。WP-CLI の wp db check は、wp-config.php の接続情報を使って mysqlcheck を実行するコマンドなので、パスワードをスクリプトに書かずに済みます。

現在の標準である InnoDB はクラッシュ後の自動復旧の仕組みを持っているため、MyISAM に比べると破損は起きにくくなっています。それでも、古いサイトに MyISAM のテーブルが残っていたり、ディスク障害が起きたりした場合に備えて、定期的に見ておく意味はあります。

WordPress のデータの関係(論理の層)

テーブルは正常でも、データ同士の関係が崩れていることがあります。代表的なものは次のとおりです。

  • 孤立した postmeta: 投稿が消えたのに、その投稿のカスタムフィールドが残っている
  • 孤立した term_relationships: 存在しない投稿にカテゴリーやタグの関連付けが残っている
  • 孤立したコメントと commentmeta: 投稿やコメントが無いのに付属データが残っている
  • 孤立した usermeta: 削除済みユーザーのメタデータが残っている
  • 自動読み込み(autoload)されるオプションの肥大化: 毎回のページ表示で読み込まれるデータが大きくなっている

これらはサイトがすぐ壊れる原因にはなりにくいものの、テーブルの肥大化や、プラグインの削除漏れ、移行時の不整合の手がかりになります。件数の推移を定期的に記録しておくと、急に増えたときに気づけます。

チェックスクリプトの例

WP-CLI が使えるサーバー向けの Bash スクリプトです。SELECT と wp db check だけを実行し、データは変更しません。

#!/usr/bin/env bash
# WordPress DB 整合性チェック(読み取り専用。修復・削除は行わない)
set -uo pipefail

WP_PATH="/var/www/example.com/public"   # WordPress のインストール先に合わせる
LOG_DIR="$HOME/wp-db-check"             # ログの保存先(Web から見えない場所)
mkdir -p "$LOG_DIR"
LOG="$LOG_DIR/check-$(date +%Y%m%d-%H%M).log"
WARN=0

wpq() {
  wp --path="$WP_PATH" db query "$1" --skip-column-names
}

check_count() {
  local label="$1" sql="$2" n
  n=$(wpq "$sql") || { echo "[ERROR] ${label}: クエリ失敗"; WARN=1; return; }
  echo "${label}: ${n}"
  if [ "${n:-0}" -gt 0 ]; then WARN=1; fi
}

P=$(wp --path="$WP_PATH" db prefix)   # テーブル接頭辞(例: wp_)

{
  echo "== $(date '+%F %T') テーブルの検査(wp db check) =="
  if ! out=$(wp --path="$WP_PATH" db check 2>&1); then WARN=1; fi
  echo "$out"
  if echo "$out" | grep -qiE "error|corrupt|warning"; then WARN=1; fi

  echo "== 孤立データの件数 =="
  check_count "postmeta(投稿なし)" \
    "SELECT COUNT(*) FROM ${P}postmeta pm LEFT JOIN ${P}posts p ON p.ID = pm.post_id WHERE p.ID IS NULL;"
  check_count "term_relationships(投稿なし)" \
    "SELECT COUNT(*) FROM ${P}term_relationships tr
       JOIN ${P}term_taxonomy tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
       LEFT JOIN ${P}posts p ON p.ID = tr.object_id
     WHERE p.ID IS NULL AND tt.taxonomy <> 'link_category';"
  check_count "comments(投稿なし)" \
    "SELECT COUNT(*) FROM ${P}comments c LEFT JOIN ${P}posts p ON p.ID = c.comment_post_ID WHERE p.ID IS NULL;"
  check_count "commentmeta(コメントなし)" \
    "SELECT COUNT(*) FROM ${P}commentmeta cm LEFT JOIN ${P}comments c ON c.comment_ID = cm.comment_id WHERE c.comment_ID IS NULL;"
  check_count "usermeta(ユーザーなし)" \
    "SELECT COUNT(*) FROM ${P}usermeta um LEFT JOIN ${P}users u ON u.ID = um.user_id WHERE u.ID IS NULL;"

  echo "== 自動読み込みオプションの合計サイズ(KB) =="
  wpq "SELECT ROUND(SUM(LENGTH(option_value))/1024) FROM ${P}options WHERE autoload IN ('yes','on','auto','auto-on');"
} > "$LOG" 2>&1

# 異常があれば終了コード 1(cron の通知や監視サービスで拾う)
if [ "$WARN" -ne 0 ]; then
  cat "$LOG"
  exit 1
fi

スクリプトのポイント

  • 接続情報を書かない: wp db check と wp db query は wp-config.php の接続情報を使うため、スクリプトやログにパスワードが残りません。
  • 接頭辞を自動で取得する: wp db prefix で接頭辞を取り出し、wp_ 以外の接頭辞でも動くようにしています。
  • link_category を除外する: term_relationships の object_id は、古いリンク機能のカテゴリーではリンクの ID を指すため、投稿が無くても正常です。
  • autoload の値: WordPress 6.6 で autoload 列の値に on・off・auto などが加わったため、従来の yes とあわせて数えています。
  • 異常時だけ出力する: 正常時は何も表示せず、異常時にログの内容を表示して終了コード 1 で終わります。cron はジョブが何かを出力するとメールを送る仕組みなので、異常時だけ通知が届きます。

wp db check の出力形式は、MySQL/MariaDB のバージョンによって多少異なります。最初に手動で実行し、正常時の出力を確認してから grep の条件を調整してください。

定期実行の設定

WordPress のファイルを所有しているユーザー(Web サーバーの実行ユーザーなど)の crontab に登録します。root で WP-CLI を動かすと警告が出るうえ、権限の面でも好ましくありません。

# crontab -e で追記。毎週月曜 4:30 に実行し、異常時はメールで通知する
MAILTO=admin@example.com
30 4 * * 1 nice -n 10 /home/deploy/bin/wp-db-check.sh

メール送信の仕組みがサーバーに無い場合は、死活監視サービスの ping URL をスクリプトの最後で呼ぶなど、別の通知方法を用意します。

CHECK TABLE は、テーブルの大きさによっては時間がかかり、その間に書き込みが待たされることがあります。アクセスの少ない時間帯に設定し、まずは手動で実行して所要時間を測ってください。週1回程度で十分なサイトがほとんどです。

異常が見つかったときの判断

テーブルの破損が出た場合

最初にバックアップを取ります(wp db export)。そのうえで、ステージング環境にコピーして修復を試します。MyISAM のテーブルなら wp db repair(mysqlcheck --repair)で直ることがあります。InnoDB では REPAIR TABLE が使えないため、バックアップからの復元や、データベースのログの確認が必要になります。原因がディスクの障害であれば、修復より先にサーバー側の対応が必要です。

孤立データが見つかった場合

件数が少なければ、急いで対処する必要はありません。件数が多い、または急に増えた場合は、どのメタキーが多いかを確認して、原因になったプラグインを探します。

-- 接頭辞が wp_ の例。孤立した postmeta をメタキー別に数える
SELECT pm.meta_key, COUNT(*) AS cnt
FROM wp_postmeta pm
LEFT JOIN wp_posts p ON p.ID = pm.post_id
WHERE p.ID IS NULL
GROUP BY pm.meta_key
ORDER BY cnt DESC
LIMIT 20;

削除する場合は、バックアップを取ってから、ステージングで同じ削除を試し、表示や機能に問題が無いことを確認したうえで本番に適用します。カテゴリーやタグの記事数がずれている場合は、wp term recount category post_tag のように対象のタクソノミーを指定して数え直せます。

自動読み込みのサイズが大きい場合

サイズの大きいオプションを option_name ごとに調べ、どのプラグインのものかを確認します。サイトヘルスの画面でも、自動読み込みされるオプションの大きさに関する確認項目が表示されることがあります。この項目は WordPress 6.6 以降で追加されたもので、表示条件や文言はバージョンによって変わる可能性があります(2026年10月現在)。

マルチサイトの場合

マルチサイトでは、サイトごとに wp_2_posts のような接頭辞の付いたテーブルがあり、ユーザーのテーブルはネットワークで共有されます。上のスクリプトは1サイト分の接頭辞だけを見るため、wp site list で各サイトの URL を取り出し、--url を付けてサイトごとに実行する形に広げてください。

まとめ

  • 整合性のチェックは、テーブルの破損(wp db check)と、データ同士の関係(孤立データを数える SQL)の2段階で行う。
  • スクリプトは読み取り専用にし、修復や削除は内容を確認してから人が行う。
  • 接続情報は wp-config.php から読ませ、接頭辞は wp db prefix で取得する。
  • cron は WordPress のファイル所有ユーザーで、アクセスの少ない時間帯に実行し、異常時だけ通知が届くようにする。
  • 修復や削除の前にはバックアップを取り、ステージングで試してから本番に適用する。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲