ACIDのCだけ腑に落ちないのはなぜか——RDBMSを自作して確かめた

⚖️

September 7, 2026

はじめに

データベースのトランザクションが満たすべき性質として、ACID——Atomicity(原子性)、Consistency(整合性)、Isolation(独立性)、Durability(永続性)——が知られています。
このうちA・I・Dの定義は明快です。原子性は「全部実行されるか、まったく実行されないかのどちらかである」、独立性は「並行して実行されるトランザクションが互いに干渉しない」、永続性は「コミットされた結果が失われない」。いずれも、何が保証されるのかを一文で言い切れます。
ところがCだけは、読んでも像を結びません。なぜCだけが分かりにくいのかを整理します。

なぜCだけ分かりにくいのか

理由は2つあると考えています。
1つ目は、多くの解説が循環定義になっていることです。
Consistencyは文献によって「一貫性」とも「整合性」とも訳されます。
この事情が、循環定義を生みます。Consistencyの説明を複数並べて読むと、その多くが「トランザクションの前後で一貫性が保たれること」あるいは「整合性が保たれること」という形をとっています。つまり、同じ語の2つの訳を互いの説明に使っているわけです。訳し分けられているために気づきにくいのですが、これは「Consistencyとは、Consistencyが保たれることである」と言っているのと変わりません。では整合性とは何なのか、という肝心の問いには答えていないのです。
2つ目は、CだけがACIDの中で毛色が違うのに、そのことが述べられないままであることです。
A・I・Dは「データベースがどう振る舞うべきか」を定める性質です。途中までしか実行されていない状態を作るな、並行するトランザクションの相互作用を制御せよ、コミットした結果を失うな。いずれも振る舞いへの要求です。一方でCは「どの状態が正しいのか」を定めます。正しさの中身が外から与えられない限り、Cは内容を持ちません。同じ4文字の並びに置かれているために同種のものだと期待して読み、期待が外れて混乱する、という構造になっています。

Kleppmannの整理

この点を明快に述べているのが、Martin Kleppmann『データ指向アプリケーションデザイン』です。同書はp.243で次のように書いています。
ACIDにおける一貫性という概念は、データについて常に真でなければならない何らかの言明(不変性)があるということです。
(中略)
とはいえ、この一貫性の概念はアプリケーション固有の不変性の概念に依存しており、一貫性を保つようにトランザクションを適切に定義することはアプリケーションの責任になります。これはデータベースが保証できることではありません。アプリケーションが不変性に違反する悪いデータを書き込んだとしても、データベースはそれを止めることはできません。
そして同じページで、こう言い切っています。
原子性、分離性、永続性はデータベースの特性ですが、一貫性は(ACIDという考え方においては)アプリケーションの特性です。(中略)したがって、Cは実際にはACIDに属していないのです。
「Cは実際にはACIDに属していない」。教科書で当然のように並べられている4文字に対して、かなり強い主張です。

実装するとどこに差が出るか

私はEdward Sciore『Database Design and Implementation』の教育用RDBMS「SimpleDB」をRustへ移植した sabidb を書いてきました。トランザクション周りは自分で実装しているので、A・I・Dのそれぞれに何が必要だったかを具体的に言えます。
原子性と永続性には、ログとリカバリの機構が要ります。 src/tx/recovery/ には、トランザクションの開始・コミット・ロールバック・チェックポイント、および更新前の値を記録するログレコードの型が並んでいます。リカバリマネージャは commit / rollback / recover を提供し、ロールバック時にはログを遡って更新前の値を書き戻し、起動時には未完了のトランザクションを取り消します。write-ahead loggingの規律——データページより先にログを永続化する——を守らなければ、この機構は成立しません。
独立性には、並行制御の機構が要ります。 src/tx/concurrency/ にロックテーブルを置き、ブロック単位で共有ロックと排他ロックを管理しています。
では整合性には何が要ったか。何も要りませんでした。
ConsistencyManager のようなモジュールは存在しませんし、書く必要も生じませんでした。それどころか、sabidbにはNOT NULL制約もUNIQUE制約も実装されていません。制約チェックのコードは一行もないのに、このデータベースはトランザクションを処理し、ロールバックし、クラッシュから復旧します。
A・I・Dはそれぞれ固有の機構を要求してきたのに、Cに対応する機構だけが存在しませんでした。正確に言えば「Cには何も要らない」のではなく、Cを単独で実現する機構が無いということです。
もちろん、実運用のRDBMSは外部キー制約・UNIQUE制約・CHECK制約といった機構を備えており、整合性の一部をデータベース側で担保できます。Kleppmann自身も、ある種の不変性はデータベースがチェックできると同じ箇所で断っています。ただしそれらの制約は、業務上のルールが先に存在して初めて立ち現れるものです。データベースが自ら見つけてくるわけではありません。つまりCは、データベースの機能ではないのではなく、データベース単体では完結しないということです。スキーマとして書き下せた条件はデータベースが検証を引き受け、書き下せなかった分がアプリケーションに残ります。

状態遷移として捉え直す

対応する機構が無いからといって、Cが空虚だということにはなりません。では、Cは何を述べているのでしょうか。
トランザクションを一段抽象的に見ると、データベースの状態を別の状態へ遷移させる契機だと捉えられます。遷移先として取りうる状態は無数にありますが、その中には業務上許される状態と許されない状態があります。
たとえば口座間の送金を考えると、「送金元から引かれた額と送金先に足された額が等しい」という条件を満たす状態は許され、片方だけが更新された状態は許されません。重要なのは、この線引きをデータベースは知らないということです。どちらの状態も、データベースにとっては単に整数が書き換わっただけであり、区別する根拠を持ちません。
もう一歩踏み込んでみます。残高がマイナスになる状態は、許されないのでしょうか。
「残高がマイナスなどありえない」と言いたくなりますが、当座貸越のある口座や、与信枠を持つ決済サービスであれば、マイナス残高はまったく正常な状態です。同じ「残高 = -3,000」というデータが、ある業務では弾くべきエラーであり、別の業務では正常な状態になります。
つまり、データそのものをいくら眺めても、それが整合的かどうかは決まりません。決めているのは業務上の要求であって、データの側には答えがないのです。「整合的」という語は、データがそれ自体として備える性質を述べているように聞こえます。しかし実際には、その業務が何を整合的と定めたか、の省略形にすぎません。
データベースの状態には、そのシステムでいつでも成り立っていなければならない条件があります。送金なら「2口座の残高の合計が変わらない」、口座なら「残高がマイナスにならない」。こうした条件を不変条件(invariant)と呼びます。
この見方に立つと、Cはこう述べられます。トランザクション開始前の状態が不変条件を満たしていたならば、終了後の状態でも満たされていることを保証する。

不変条件はどこにあるのか

私自身の言葉で定義を置きます。
Consistencyとは、トランザクション処理の結果が、そのシステムの不変条件を常に満たすことを保証しなければならない、という性質である。
不変条件は一つの層にあるわけではありません。UNIQUE、外部キー、CHECK、NOT NULLのようにスキーマとして書き下せるものもあれば、業務ロジックの中にしか書けないものも、決済や在庫といった外部システムを跨いで初めて成立するものもあります。データベースが検証を引き受けられるのは、このうち書き下せた分だけです。
そしてC自体は機構ではありません。先ほどの「残高の合計が変わらない」を守るために、途中で止まらないよう原子性を使い、並行するトランザクションに乱されないよう独立性を使い、失われないよう永続性を使います。Cが「何を守るか」だとすれば、A・I・Dは「それをどのような実行モデルで守るか」を支える性質です。Cに対応する機構が無いのは、機構が要らないからではなく、Cが機構ではなく状態空間に課された条件だからです。

おわりに

ACIDのCが分かりにくいのは、説明が下手だからというより、A・I・Dと同列に置かれていること自体に無理があるからだというのが私の考えです。
A・I・Dは、データベースがどう振る舞うべきかを定めます。Cは、どの状態を正しいと呼ぶのかを定めます。この二つは、そもそも問いの種類が違います。

参考文献