こんにちは、かつコーチです。
前回はEXPLAINで実行計画を読む方法を解説しました。
ここから2本は、パフォーマンスと並んでデータベースの根幹をなす「整合性」の話に入っていきます。
以前の記事でトランザクションのCOMMITとROLLBACKを解説しましたが、そもそもトランザクションは「何を」保証してくれているのでしょうか。
その答えが、今回解説するACID特性です。
ACID特性とは?
トランザクションが満たすべき4つの性質の頭文字
ACID特性とは、データベースのトランザクションが満たすべき4つの性質、Atomicity(原子性)・Consistency(一貫性)・Isolation(独立性)・Durability(永続性)の頭文字を取った言葉です。
MySQLのInnoDBやPostgreSQLなど、多くの主要なRDBMSはこのACID特性を満たすように設計されています。
「トランザクションを使えば安全」と漠然と理解するのではなく、この4つの性質それぞれが何を保証しているのかを知ることで、なぜ安全なのかを説明できるようになります。
なぜACID特性を知る必要があるのか
ACID特性は、単なる暗記事項ではありません。
実際の障害調査やパフォーマンスチューニングの場面で、「今起きている問題はACIDのどの性質に関わる話か」を切り分けられると、原因究明のスピードが大きく変わります。
たとえば「同時アクセスで想定外のデータが見える」という問題はIsolation(独立性)の話であり、「サーバーがクラッシュしたのにデータが消えた」という問題はDurability(永続性)の話です。
次回解説する分離レベルの話も、このACID特性のうちIsolationを深掘りする内容になるため、まずは4つの性質の全体像を押さえておく必要があります。
4つの性質をひとつずつ理解する
以下、送金処理の例で解説します。
CREATE TABLE accounts (
id INT PRIMARY KEY AUTO_INCREMENT,
owner_name VARCHAR(50) NOT NULL,
balance INT NOT NULL DEFAULT 0
);
Atomicity(原子性):すべて成功かすべて失敗か
Atomicity(原子性)とは、トランザクション内のすべての処理が「すべて成功する」か「すべて失敗して何も起きなかった状態に戻る」かのどちらかになる、という性質です。
START TRANSACTION;
UPDATE accounts SET balance = balance - 10000 WHERE id = 1;
UPDATE accounts SET balance = balance + 10000 WHERE id = 2;
COMMIT;
この2つのUPDATE文の間でシステム障害が起きたとしても、原子性が保証されていれば、COMMITされていない変更はすべてなかったことになります。
「送金元からは引かれたのに送金先には届かない」という中途半端な状態は、原子性によって防がれます。
Consistency(一貫性):制約やルールに違反しない
Consistency(一貫性)とは、トランザクションの前後で、データベースに定義された制約やビジネスルールが常に守られている、という性質です。
CREATE TABLE accounts (
id INT PRIMARY KEY AUTO_INCREMENT,
owner_name VARCHAR(50) NOT NULL,
balance INT NOT NULL CHECK (balance >= 0)
);
たとえば残高がマイナスにならないというCHECK制約を設定しておけば、送金処理によって残高がマイナスになるようなUPDATE文は、トランザクションごと失敗します。
一貫性は、NOT NULLやUNIQUE、外部キー制約といったこれまでの記事で紹介した制約群が、トランザクションを通じて常に守られ続けることを保証する性質だとイメージすると分かりやすいです。
Isolation(独立性):同時実行中の処理が互いに干渉しない
Isolation(独立性)とは、複数のトランザクションが同時に実行されても、互いに影響を及ぼし合わず、あたかも1つずつ順番に実行されたかのような結果になる、という性質です。
-- トランザクションA
START TRANSACTION;
UPDATE accounts SET balance = balance - 10000 WHERE id = 1;
-- まだCOMMITしていない
-- 別のセッションでトランザクションB
SELECT balance FROM accounts WHERE id = 1;
トランザクションAがCOMMITする前に、トランザクションBがaccountsテーブルを参照した場合、変更前・変更後のどちらの値が見えるべきかは、独立性の「強さ」によって変わってきます。
この「独立性の強さ」を段階的に設定する仕組みがトランザクション分離レベルであり、次回の記事で詳しく解説します。
Durability(永続性):COMMITした変更は失われない
Durability(永続性)とは、一度COMMITされたトランザクションの結果は、その後にサーバーがクラッシュしたり停電が起きたりしても失われない、という性質です。
MySQL(InnoDB)は、COMMITされた変更をディスク上のログファイルに書き込んでから応答を返す仕組みになっており、これによって永続性を実現しています。
「COMMITした瞬間にディスクへの書き込みも保証されている」と理解しておくと、なぜCOMMIT直後にサーバーが落ちてもデータが残るのかが腑に落ちます。
つまずきやすいポイント:4つの性質を混同する
原子性と一貫性の違いが分かりにくい
❌ Before:「トランザクションが安全」という言葉で4つの性質をまとめて理解してしまう
「トランザクションを使えばデータの整合性が保たれる」という理解のまま止まっていると、実際に障害が起きたときに「具体的に何が壊れたのか」を説明できません。
私が実際にレビューした案件でも、「トランザクションを使っているのになぜかデータがおかしくなった」という報告を受けて調査したところ、原因は原子性の欠如ではなく、アプリケーション側のバリデーション漏れによる一貫性の問題だった、というケースがありました。
原因を「トランザクションの設定ミス」だと決めつけて時間を浪費してしまったのが反省点です。
✅ After:4つの性質を切り分けて、問題がどこにあるかを特定する
-- 一貫性を守るための制約を、データベース側にも明示的に持たせる
CREATE TABLE accounts (
id INT PRIMARY KEY AUTO_INCREMENT,
owner_name VARCHAR(50) NOT NULL,
balance INT NOT NULL CHECK (balance >= 0)
);
「途中で処理が止まって不完全な状態になった」なら原子性、「制約に違反したデータが入ってしまった」なら一貫性、「同時アクセスで想定外の値が見えた」なら独立性、「確定したはずのデータが消えた」なら永続性、というように、起きている現象を4つの性質のどれに当てはまるか整理してから調査を始めると、原因究明が早くなります。
アプリケーション側のバリデーションだけに頼らず、データベース側にも制約を持たせておくことで、一貫性をより確実に守れます。
応用・一歩先の使い方
NoSQLとBASE特性との対比
RDBMSがACID特性を重視するのに対し、MongoDBなどの一部のNoSQLデータベースは、可用性やスケーラビリティを優先するBASE特性(Basically Available、Soft state、Eventually consistent)という考え方を採用することがあります。
| 特性 | 重視する点 | 代表的なDB |
|---|---|---|
| ACID | 厳密な整合性・信頼性 | MySQL、PostgreSQLなどのRDBMS |
| BASE | 可用性・スケーラビリティ | MongoDBなど一部のNoSQL |
金融取引のように1円の誤差も許されない領域ではACID特性を重視したRDBMSが選ばれやすく、SNSのタイムラインのように多少の遅延が許容される領域ではBASE特性寄りの設計が選ばれることがあります。
「絶対に整合性が必要な処理か」「多少の遅延は許容できる処理か」という観点は、DB製品を選定する際の判断軸の1つになります。
AtomicityとDurabilityを支えるログの仕組み
InnoDBは、REDOログと呼ばれる仕組みを使って原子性と永続性を実現しています。
変更内容をまずログファイルに書き込み、その後で実データに反映するという流れによって、途中でクラッシュが起きてもログから変更内容を復元できるようになっています。
こうした内部の仕組みまで知っておく必要は日常的にはありませんが、「なぜCOMMIT後のデータは消えないのか」という疑問を突き詰めていくと、こうしたログの仕組みに行き着くという点は知っておくと理解が深まります。
まとめ
この記事のポイント
- ACID特性はトランザクションが満たすべき4つの性質、Atomicity・Consistency・Isolation・Durabilityの総称
- Atomicity(原子性)はすべて成功かすべて失敗かを、Consistency(一貫性)は制約やルールの遵守を保証する
- Isolation(独立性)は同時実行中の処理同士が干渉しないことを、Durability(永続性)はCOMMIT後のデータが失われないことを保証する
- 起きている問題を4つの性質のどれに当てはまるか切り分けると、原因究明が速くなる
次に読むべき記事
4つの性質のうち、Isolation(独立性)には実は「強さ」の段階があり、その設定次第でダーティリードやファントムリードといった現象が起きることがあります。
次回はSQL基礎編の最終回として「トランザクション分離レベルとダーティリード・ファントムリード」を解説します。
タグ: SQL, 中級者向け, トランザクション