MySQL道普請便り

第268回pt-online-schema-changeで外部キー制約付きテーブルにDDLを実行する

外部キー制約が設定されているテーブルに対してDDLを実行したい場面は、運用していると意外と発生します。しかし、MySQLにおける外部キー制約は単一テーブルの問題ではなく、親子関係にあるテーブル全体に影響を及ぼすため、単純なDDLとは異なる注意が必要です。

一方で、オンラインスキーマ変更の手段としてはpt-online-schema-change(pt-osc)やgh-ostがよく利用されますが、外部キー制約が存在する場合gh-ostは制約上使用できません。pt-oscは外部キー制約に対応するための--alter-foreign-keys-methodというオプションが用意されています。そのため、外部キー制約が関係するDDLではpt-oscの利用が現実的な選択肢となります。

本稿では、pt-oscの--alter-foreign-keys-methodに着目し、その内部挙動と運用上の注意点を整理します。

pt-online-schema-changeにおける外部キー制約対応

pt-oscの--alter-foreign-keys-methodでは、外部キー制約が存在する場合の付け替え方法を制御できます。指定可能なモードは以下の通りです。

モード 動作
drop_swap foreign_key_checks=0に設定して旧テーブルを削除→新テーブルをリネーム
rebuild_constraints 子テーブルに対してALTER TABLEを実行し、外部キー制約をDROP→ADDする
auto 条件に応じてrebuild_constraintsまたはdrop_swapを自動選択する
none 外部キー制約を放置する (参照先が存在しない状態になる)

drop_swapは外部キー制約を一時的に無効化し、テーブルを強制的に入れ替える方式です。一時的に参照先テーブルが存在しない状態になります。

rebuild_constraintsは子テーブルの外部キー制約をDROPし、新しい親テーブルに対する外部キー制約を追加します。外部キー制約を付け替えるためのDDLALTER TABLE child_table DROP FOREIGN KEY old_fk, ADD CONSTRAINT new_fk ...が実行されます。

基本的にはrebuild_constraintsを使用するのが安全です。ここからはrebuild_constraintsについて詳しく見ていきます。

rebuild_constraintsの動作

rebuild_constraintsは、親テーブルの変更後に子テーブル側の外部キー制約を一度削除し、新しい親テーブルに対して再作成します。

  1. 親テーブルに対するDDL(pt-osc本体)
  2. 子テーブルに対する外部キー制約付け替え(ALTER TABLE)

pt-oscのソースコードを確認してみると、子テーブルに対する付け替え処理の中でforeign_key_checks=0に設定されます[pt-online-schema-change:11877-11884]。そのため、子テーブルの外部キー制約の付け替え処理はオンラインで実行されます。このforeign_key_checksはセッション単位で設定されるため、他セッションで外部キー制約の整合性チェックが無効化されることはありません。

実行例

実際にrebuild_constraintsを利用してDDLを実行する例を示します。テスト用にシンプルなテーブルを作成して実行しました。

mysql> SHOW CREATE TABLE parent\G
*************************** 1. row ***************************
       Table: parent
Create Table: CREATE TABLE `parent` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

mysql> SHOW CREATE TABLE child\G
*************************** 1. row ***************************
       Table: child
Create Table: CREATE TABLE `child` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `parent_id` bigint NOT NULL,
  `note` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_child_parent` (`parent_id`),
  CONSTRAINT `fk_child_parent` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

外部キー制約があるテーブルに対してpt-oscは、たとえば以下のようなコマンドで実行できます。

$ pt-online-schema-change \
  "D=test,t=parent" \
  --alter "ADD COLUMN created_at TIMESTAMP NULL DEFAULT NULL" \
  --alter-foreign-keys-method=rebuild_constraints \
  --set-vars "lock_wait_timeout=2" \
  --host 127.0.0.1 \
  --user root \
  --execute
No replicas found.  See --recursion-method if host has replicas.
Not checking replica lag because no replicas were found and --check-replica-lag was not specified.

Operation, tries, wait:
  analyze_table, 10, 1
  copy_rows, 10, 0.25
  create_triggers, 10, 1
  drop_triggers, 10, 1
  swap_tables, 10, 1
  update_foreign_keys, 10, 1
Child tables:
  `test`.`child` (approx. ? rows)
Will use the rebuild_constraints method to update foreign keys.
Altering `test`.`parent`...
Creating new table...
Created new table test._parent_new OK.
Altering new table...
Altered `test`.`_parent_new` OK.
2026-03-30T21:11:11 Creating triggers...
2026-03-30T21:11:11 Created triggers OK.
2026-03-30T21:11:11 Copying approximately 0 rows...
2026-03-30T21:11:11 Copied rows OK.
2026-03-30T21:11:11 Rebuilding foreign key constraints...
2026-03-30T21:11:11 Rebuilt foreign key constraints OK.
2026-03-30T21:11:11 Analyzing new table...
2026-03-30T21:11:11 Swapping tables...
2026-03-30T21:11:11 Swapped original and new tables OK.
2026-03-30T21:11:11 Dropping old table...
2026-03-30T21:11:11 Dropped old table `test`.`_parent_old` OK.
2026-03-30T21:11:11 Dropping triggers...
2026-03-30T21:11:11 Dropped triggers OK.
Successfully altered `test`.`parent`.

外部キー制約があるテーブルに対してもDDLが実行できていることが確認できます。ログを見ると、親テーブルへのDDLの実行後に外部キー制約の付け替えが行われていることがわかります。

一点、元々の外部キー制約名の先頭にアンダーバーが付与される形で、外部キー制約付け替え時に制約名が変更される点には注意が必要です。

mysql> SHOW CREATE TABLE parent\G
*************************** 1. row ***************************
       Table: parent
Create Table: CREATE TABLE `parent` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.003 sec)

mysql> SHOW CREATE TABLE child\G
*************************** 1. row ***************************
       Table: child
Create Table: CREATE TABLE `child` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `parent_id` bigint NOT NULL,
  `note` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `_fk_child_parent` (`parent_id`),
  CONSTRAINT `_fk_child_parent` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.001 sec)

実行にあたっての注意点

ここまで見てきたように、rebuild_constraintsを用いることで外部キー付きテーブルにもDDLを適用できますが、いくつか運用上注意すべき点があります。

1. インデックスの再作成

外部キー制約には、子テーブル側に適切なインデックスが必要です。存在しない場合は、外部キー制約作成時にインデックスが自動で作成されます。

rebuild_constraintsは外部キー制約をDROP→ADDするため、このような自動で生成されたインデックスを使用している場合、外部キー制約の付け替え時にインデックスの再作成が発生することがあります。

この処理は子テーブルに対するALTER TABLEとして実行されるため、テーブルサイズが大きい場合は負荷が高くなり、Source/Replica構成ではレプリケーション遅延の原因となる可能性があります。

そのため、外部キー列については事前に明示的なインデックスを定義しておくと安全です。外部キー制約におけるインデックスの挙動については、第63回 MySQLの外部キー(Foreign Key)制約が参考になります。

2. 複数子テーブルと部分失敗

rebuild_constraintsは、子テーブルを1つずつ順番に処理します。つまり、複数の子テーブルが存在する場合、以下のような状態が起こり得ます。

  • 子テーブルA:成功(外部キー制約付け替え済み)
  • 子テーブルB:失敗(途中でエラー)

この場合、Aの変更はロールバックされません。すなわち、一部の子テーブルだけが新しい親テーブルを参照し、他は未変更のまま残るという不整合な状態が発生します。

これは子テーブルに対する外部キー制約の付け替えが個別のALTER TABLEとして順次実行されるためであり、pt-osc側でも失敗時に全体を巻き戻す仕組みにはなっていません。

そのため、運用としては以下のような制約を設けるのが安全です。

  • 子テーブルが1つの場合のみ実行する
  • 複数ある場合は事前に手動で整理する
  • 失敗時の復旧手順を用意しておく

まとめ

rebuild_constraintsは、外部キー制約が存在する環境でもpt-oscを適用可能にする便利な機能です。しかし内部的には子テーブルに対して外部キー制約付け替えDDLを実行する方式であり、どのように動作するのかとその注意点を理解しておくことが大事です。

特に以下の点は事前に確認しておく必要があります。

  • 外部キー列のインデックスが適切に定義されているか
  • 子テーブルが複数存在しないか

これらを踏まえたうえで利用すれば、外部キー制約があるテーブルに対するDDLであっても安全に運用することができます。

おすすめ記事

記事・ニュース一覧