
こんにちは、デリッシュキッチン開発部の鈴木です。
概要
Data Definition Language(DDL、データ定義言語)、つまり CREATE TABLE や ALTER TABLE でスキーマを変更する操作を、トランザクションの中で実行してあとからロールバックできるかは、データベースによって違います。MySQL ではロールバックできず、PostgreSQL ではロールバックできます。
ロールバックは、1 つのトランザクションの中で行った変更を、記録しておいた変更前の状態に戻す操作です。だからロールバックするには、その操作が 1 つのトランザクションに収まっていること、そしてその変更が記録されていることの 2 つが必要です。前者はどのデータベースでも同じで、1 つのトランザクションに収まらない操作(PostgreSQL の CREATE INDEX CONCURRENTLY など)は、そもそもロールバックの対象になりません。データベースによって分かれるのは、後者です。
ロールバックできるか、つまり変更が記録されるかは、メタデータの置き場所で決まります。変更前の状態は、ストレージエンジンの内部に記録されます。そのため、スキーマの定義情報であるメタデータも、その内部になければロールバックできません。古い MySQL はメタデータをエンジンの外側のファイルに置いていたため、ロールバックできません。PostgreSQL はメタデータを通常のテーブルとして内部で管理するため、ロールバックできます。MySQL 8.0 はメタデータを内部に移しましたが、互換性のために暗黙的なコミットを残したため、ロールバックできません。MySQL と PostgreSQL の差は、この置き場所の違いから生まれます。

図1:DDL をロールバックできるかの全体像。1 つのトランザクションに収まるか(DB 共通)→ 収まればロールバックできるか(DB で違う)の順に決まる。
前提:スキーマ変更をロールバックできるかは、データベースで違う
CREATE TABLE や ALTER TABLE といった DDL を、トランザクションの中で実行してあとからロールバックできるかは、データベースによって違います。
- MySQL:ロールバックできません。DDL には暗黙的なコミットが伴い、
ALTER TABLEを実行した時点でそれまでの変更が確定します。あとからROLLBACKしても戻りません。 - PostgreSQL:ロールバックできます。
BEGIN; ALTER TABLE ...; ROLLBACK;で変更前の状態に戻ります。
本記事はこの差を前提とし、この違いを生んでいるものは何かを扱います。
DDL をロールバックできるかは、収まるか → ロールバックできるか の順で決まる
ロールバックは、1 つのトランザクションの中で行った変更を、記録しておいた変更前の状態に戻す操作です。だからロールバックするには、その操作が 1 つのトランザクションに収まっていること、そしてその変更が記録されていること、の 2 つが必要です。前者を満たさなければ後者には進めないので、まず収まるか、次にロールバックできるか、の順に確かめれば十分です。
- 1 つのトランザクションに収まるか。 実行のしかたの問題で、データベースによらず一律に効きます。
- 収まるとして、その変更をロールバックできるか。 メタデータ(テーブルや列などスキーマの定義情報)の置き場所で決まり、データベースによって変わります。
ロールバックできるかを問えるのは、1 つのトランザクションに収まる操作だけです。前提で見た MySQL と PostgreSQL の差は、ロールバックできるかで決まります。
1 つのトランザクションに収まるか(DB によらず一律)
ほとんどの DDL は 1 つのトランザクションに収まります。ただし、収まらない操作もあります。これはデータベースによりません。
代表例は PostgreSQL の CREATE INDEX CONCURRENTLY です。通常の CREATE INDEX はテーブルをロックして一気に索引を作ります。一方 CONCURRENTLY は、書き込みを止めずに索引を作るため、複数の段階に分けて実行します。各段階はそれぞれ別のトランザクションになるので、全体を 1 つのトランザクションブロックに収められません。
DDL をロールバックできる PostgreSQL でも、CREATE INDEX CONCURRENTLY はトランザクションの中では実行できません。ロールバックできるかどうか以前に、1 つのトランザクションに入らないからです。この操作はここで止まり、ロールバックできるかはそもそも問えません。
収まるとして、ロールバックできるか(ここで DB が分かれる)
1 つのトランザクションに収まる操作、つまり普通の DDL について、ロールバックできるかを考えます。これがデータベースによって違います。
行の更新や削除といった Data Manipulation Language(DML、データ操作言語)をロールバックできるのは、MVCC(変更前の古い行を残しておく仕組み)が古い行を保持しているからです。この記録の仕組みはストレージエンジンの内部にあります。だから DDL をロールバックするには、スキーマを記述するメタデータも、ストレージエンジンの内部、つまりこの仕組みの管理下になければなりません。メタデータがこの管理下にあるかどうかが、データベースごとの差を生みます。
- 古い MySQL(5.7 以前):メタデータは
.frmというファイルにあり、ストレージエンジンの外側にありました。ロールバックの仕組みの管理下にないので、トランザクションに参加できません。だから暗黙的にコミットされ、ロールバックできません。 - MySQL 8.0:メタデータを InnoDB の内側に取り込み、データディクショナリとして管理するようにしました。これで DDL がアトミックになりました(途中で中断しても中途半端なスキーマが残りません)。ただし、この変更が狙ったのはクラッシュ時の安全性であって、ユーザーがロールバックできるようにすることではありません。従来の挙動との互換性から、暗黙的なコミットはそのまま残されました。アトミック(壊れない)と、ユーザーが好きなときにロールバックできることは別物です。 内側に入れても、まだロールバックできません。
- PostgreSQL:スキーマ情報をシステムカタログ(
pg_classなど)として、通常のテーブルと同じく MVCC で管理します。CREATE TABLEはシステムカタログに 1 行追加するだけの操作になります。行の追加をロールバックするのと同じ仕組みがそのまま効くので、ロールバックできます。
3 つは別々のケースではなく、メタデータの置き場所が、外側(古い MySQL)から、内側だが暗黙コミット(MySQL 8.0)、内側で完全管理(PostgreSQL)へと段階的に変わっているだけです。前提で見たとおり、MySQL はロールバックできず、PostgreSQL はロールバックできます。これは、このメタデータの置き場所による結果です。

図2(詳細):DDL をロールバックできるかは、1 つのトランザクションに収まるか(DB 共通)→ 収まるとしてロールバックできるか(メタデータの置き場所・DB で違う)の順に決まる。
結論:DDL かどうかではなく、収まるか → ロールバックできるか で見る
DDL をロールバックできるかは、収まるか、次にロールバックできるか、を順に確かめれば分かります。DDL かどうかで一律に判断すると、PostgreSQL ではロールバックできることや、PostgreSQL でも CREATE INDEX CONCURRENTLY はロールバックできないことを見落とします。この順で見れば、そうした例外も正しく判断できます。
- 1 つのトランザクションに収まるか。 収まらなければ(
CREATE INDEX CONCURRENTLYなど)、どのデータベースでもロールバックできません。 - 収まるとして、メタデータがロールバックの仕組みの管理下にあるか。 使っているデータベースによって、ロールバックできるかが決まります。
最後に、具体的な落とし穴を一つ挙げます。MySQL では DDL と DML を同じトランザクションに混ぜてはいけません。これはロールバックできるか(メタデータの置き場所)に関わる問題です。MySQL では DDL のメタデータがロールバックの仕組みの外側にあるため、ALTER の暗黙的なコミットによって、直前の INSERT などが意図せず確定してしまいます。この挙動は、DDL がロールバックの仕組みの外側にあることから説明できます。
まとめ
今回は、なぜ MySQL では DDL をロールバックできず PostgreSQL ではできるのかについて考えました。MySQL は DDL をロールバックできない、PostgreSQL はできるということを覚えてもいいとは思うのですが、それではツールの使い方に閉じてしまいます。なぜそのような設計になっているのかを理解することで、MySQL や PostgreSQL という個別の知識にとどまらず、初めて触れる操作やデータベースでも、1 つのトランザクションに収まるか、そしてメタデータがロールバックの仕組みの管理下にあるか、という同じ見方で自分で判断できるようになります。実際、同じ PostgreSQL でも CREATE INDEX CONCURRENTLY はロールバックできない、といった例外にも自分で気づけるでしょう。