2026年5月24日

2026年5月24日

WordPressのスキーマ変更を安全に行う手順(バックアップ・dbDelta・オンラインDDL)

テーブルに列を追加する、インデックスを作る、型を変える、といったスキーマ(テーブル定義)の変更は、失敗するとサイトが止まったりデータを失ったりします。この記事では、WordPress でスキーマを変更するときの進め方を、準備・実行・確認・戻し方の順に説明します。

最初に: コアテーブルは変更しない

wp_posts や wp_users など WordPress コアのテーブルに独自の列を追加するのは避けます。コアのテーブル定義は WordPress が管理しており、独自の列はプラグインやコアの処理から見えず、移行ツールやバックアップ製品によっては扱われないことがあります。

追加の情報を持たせたい場合は、次の方法を使います。

持たせたい情報 置き場所
投稿ごとの追加項目 投稿メタ(update_post_meta()、wp_postmeta)
ユーザーごとの追加項目 ユーザーメタ(update_user_meta())
サイト全体の設定 オプション(update_option())
大量の行・独自の検索条件が必要なデータ プラグインが作る独自テーブル

スキーマ変更が実際に必要になるのは、主に独自テーブル(自作プラグインのテーブルなど)を変更する場合です。

手順1: 完全なバックアップと戻し方を用意する

変更の前に、データを含めたデータベース全体を書き出します。テーブル定義だけ(mysqldump --no-data)のバックアップでは、データを戻せません。

# WP-CLI を使う場合
wp db export before-schema-change-$(date +%Y%m%d%H%M).sql

# mysqldump を使う場合(InnoDB を止めずに一貫した状態で書き出す)
mysqldump -u ユーザー名 -p --single-transaction --routines --triggers \
  データベース名 > before-schema-change.sql

-p の後にはパスワードを書かず、入力を求めさせます。書き出したら、ファイルのサイズが0でないこと、末尾に -- Dump completed の行があることを確かめます。

戻し方も事前に決めておきます。

  • 変更を取り消す SQL(例: 追加した列を削除する ALTER TABLE ... DROP COLUMN ...)を用意する
  • それでも戻せない場合に、バックアップから戻す手順(wp db import)と所要時間を把握する

バックアップから戻すと、バックアップ以降に入った注文やコメントも消える点に注意してください。

手順2: ステージングで試す

本番のデータベースをステージング環境に複製し、同じ変更を実行します。確認するのは次の点です。

  • SQL がエラーなく終わるか
  • 本番と同じ量のデータで、何秒かかるか
  • 変更後に、サイトの表示や管理画面、関係するプラグインの機能が動くか

変更前後のテーブル定義は SHOW CREATE TABLE で比べます。

SHOW CREATE TABLE wp_myplugin_log\G

手順3: プラグインの独自テーブルは dbDelta で管理する

自作プラグインのテーブルは、WordPress の dbDelta() 関数で作成・更新するのが一般的です。dbDelta() は CREATE TABLE 文と今のテーブルを比べ、足りない列やインデックスを追加し、列の型の違いを変更します。テーブル定義のバージョンをオプションに保存し、バージョンが変わったときだけ実行します。

<?php
// プラグインのメインファイル
define( 'MYPLUGIN_DB_VERSION', '1.1' );

function myplugin_install_table() {
	global $wpdb;

	$table_name      = $wpdb->prefix . 'myplugin_log';
	$charset_collate = $wpdb->get_charset_collate();

	$sql = "CREATE TABLE $table_name (
  id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  user_id bigint(20) unsigned NOT NULL DEFAULT 0,
  action varchar(50) NOT NULL DEFAULT '',
  created_at datetime NOT NULL,
  PRIMARY KEY  (id),
  KEY user_id (user_id),
  KEY created_at (created_at)
) $charset_collate;";

	require_once ABSPATH . 'wp-admin/includes/upgrade.php';
	dbDelta( $sql );

	update_option( 'myplugin_db_version', MYPLUGIN_DB_VERSION );
}

register_activation_hook( __FILE__, 'myplugin_install_table' );

// プラグインの更新時は有効化フックが動かないため、バージョンを比べて実行する
function myplugin_maybe_upgrade_table() {
	if ( get_option( 'myplugin_db_version' ) !== MYPLUGIN_DB_VERSION ) {
		myplugin_install_table();
	}
}
add_action( 'plugins_loaded', 'myplugin_maybe_upgrade_table' );

dbDelta() は SQL の書き方に細かい決まりがあります(WordPress のプラグインハンドブックで説明されています)。

  • 列は1行に1つずつ書く
  • PRIMARY KEY と定義の間は半角スペース2つ
  • インデックスは INDEX ではなく KEY と書く
  • 列名をバッククォートで囲まない
  • 長さを取る型(varchar など)には長さを書く

dbDelta() は列やテーブルを削除しません。列名の変更は「新しい列の追加」として扱われ、古い列はデータとともに残ります。列の削除や名前の変更、データの移し替えが必要な場合は、$wpdb->query() で ALTER TABLE を実行する処理を、バージョンごとに分けて書きます。

手順4: 本番で ALTER TABLE を実行する

ALGORITHM と LOCK を指定する

MySQL 8.0 / MariaDB では、多くの ALTER TABLE をテーブルを止めずに(読み書きを受け付けたまま)実行できます。ALGORITHM と LOCK を明示すると、その方法で実行できない変更の場合はエラーで止まるため、予想外にテーブル全体のコピーやロックが起きるのを防げます。

-- 列の追加: メタデータだけの変更で済む INSTANT を指定する
ALTER TABLE wp_myplugin_log
  ADD COLUMN ip_address varchar(45) NOT NULL DEFAULT '',
  ALGORITHM=INSTANT;

-- インデックスの追加: 読み書きを止めない INPLACE / LOCK=NONE を指定する
ALTER TABLE wp_myplugin_log
  ADD INDEX action (action),
  ALGORITHM=INPLACE, LOCK=NONE;

MySQL では、ALGORITHM=INSTANT による列の追加は 8.0.12 以降で使え、8.0.29 より前はテーブルの最後の列としてだけ追加できます。どの変更がどの方式に対応するかは、MySQL / MariaDB のバージョンで異なるため、公式マニュアルの「オンライン DDL」の表で確認してください(2026年10月時点)。指定した方式に対応しない場合はエラーになるので、そのときは手順5の方法を検討します。

メタデータロックの待ちに注意する

ALTER TABLE は、実行の前後でテーブルのメタデータロックを取ります。そのテーブルを使う長いトランザクションや長いクエリが動いていると、ALTER TABLE はそれが終わるまで待ち、その後ろに来た通常のクエリも待たされて、サイトが止まったように見えることがあります。

実行前に長いクエリが無いことを確かめ、待ち時間の上限を短くしてから実行します。

-- 実行中のクエリを確認する
SHOW FULL PROCESSLIST;

-- このセッションのロック待ちを5秒までにする(超えたらエラーで中止)
SET SESSION lock_wait_timeout = 5;

ALTER TABLE wp_myplugin_log
  ADD COLUMN ip_address varchar(45) NOT NULL DEFAULT '',
  ALGORITHM=INSTANT;

タイムアウトで失敗した場合は、時間を置いて再実行します。

メンテナンスモードを使うか決める

テーブルのコピーが必要な変更や、アプリ側のコードと同時に変える必要がある変更は、アクセスの少ない時間帯にメンテナンスモードにして行います。

wp maintenance-mode activate
# スキーマ変更とプラグインの更新
wp maintenance-mode deactivate

手順5: 大きなテーブルでコピーが避けられない場合

型の変更など、テーブルの再作成が必要で、行数が多く時間がかかる変更には、Percona Toolkit の pt-online-schema-change のようなツールが使われます。新しい定義のテーブルを別に作ってデータを少しずつ写し、最後にテーブルを入れ替える仕組みです。

# まず --dry-run で確認し、問題なければ --execute で実行する
pt-online-schema-change --alter "MODIFY action varchar(100) NOT NULL DEFAULT ''" \
  D=wordpress,t=wp_myplugin_log --ask-pass --dry-run

主キーまたは一意キーが必要、トリガーを使う、作業中はディスクを多く使う、などの条件があります。使う前にツールの公式ドキュメントで条件を確認し、ステージングで試してください。

手順6: 変更後に確認する

SHOW CREATE TABLE wp_myplugin_log\G
SELECT COUNT(*) FROM wp_myplugin_log;
  • テーブル定義が意図どおりか、行数が変わっていないか
  • サイトの表示、管理画面、関係する機能が動くか
  • PHP のエラーログや DB のエラーログに新しいエラーが出ていないか
  • しばらくの間、DB の負荷や遅いクエリが増えていないか

問題が見つかったら、用意しておいた取り消しの SQL、またはバックアップからの復元で戻します。

まとめ

  • コアテーブルには列を足さず、メタやオプション、独自テーブルを使う
  • 変更前にデータを含む完全なバックアップを取り、取り消しの SQL と復元手順を決めておく(--no-data のダンプでは戻せない)
  • 独自テーブルは dbDelta() とバージョン番号のオプションで管理する。dbDelta() は列を削除しない
  • 本番の ALTER TABLE は ALGORITHM / LOCK を明示し、lock_wait_timeout でロック待ちの上限を決める
  • 大きなテーブルの再作成は、オンラインでスキーマを変えるツールをステージングで試してから使う

お気軽にご相談ください

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