3秒でわかる
DB のトランザクションが守るべき 4 つの性質の頭文字。途中で落ちても中途半端な残高や在庫を残さないための土台になる考え方です。
30秒図解
もう少し詳しく
どういうものか
ACID は、リレーショナルデータベースのトランザクションが満たすべき 4 つの性質の頭文字です。原子性 (Atomicity)、一貫性 (Consistency)、独立性 (Isolation)、永続性 (Durability) を指します。原子性は、ひとまとまりの操作が全部成功するか全部無かったことになるかのどちらかにしかならないこと。一貫性は、トランザクションの前後で外部キーや制約が壊れないこと。独立性は、同時に走る別のトランザクションから作業途中が見えないこと。永続性は、コミットが返った時点で電源が落ちても結果が残ることです。
なぜ必要か
送金処理を考えると分かりやすいです。A の口座から 1 万円引いて、B の口座に 1 万円足す。この 2 つの UPDATE の間でプロセスが落ちたら、1 万円が世界から消えます。原子性があれば、コミット前に落ちた変更はロールバックされ、引き落としも無かったことになります。
在庫も同じです。残り 1 個の商品に 2 人が同時に注文したとき、独立性が無いと両方が「残り 1 個」を読んで両方が売れてしまいます。ACID はこうした事故を、アプリ側で複雑なロック処理を書かずに DB に任せるための保証です。アプリ側で頑張って整合性を守ろうとすると、プロセスが落ちた場合の後始末まで自前で書くことになり、まず抜けが出ます。
具体例
START TRANSACTION;
UPDATE accounts SET balance = balance - 10000 WHERE id = 1;
UPDATE accounts SET balance = balance + 10000 WHERE id = 2;
-- ここで残高がマイナスなら
-- ROLLBACK; と書けば 1 件目の UPDATE も取り消される
COMMIT;似た用語との違い
| 性質 | 破れると起きること |
|---|---|
| 原子性 | 引き落としだけ成功して入金が消える |
| 一貫性 | 存在しないユーザ ID を参照する注文が残る |
| 独立性 | 同じ在庫を 2 人に売ってしまう |
| 永続性 | コミット済みの注文が再起動で消える |
分散システムでよく対比される BASE は、一貫性を緩めて可用性を優先する考え方で、NoSQL の一部が採用しています。金額を扱うなら ACID 側を選ぶのが基本です。
つまずきやすいところ
独立性は全か無かではなく段階があります。MySQL の既定は REPEATABLE READ、PostgreSQL の既定は READ COMMITTED で、同じコードでも同時実行時の見え方が変わります。「トランザクションで囲んだから安全」ではなく、どの分離レベルで動いているかを確かめる必要があります。
もう 1 つの落とし穴は DDL です。MySQL では ALTER TABLE や CREATE TABLE を実行すると暗黙にコミットが走り、それまでの変更を ROLLBACK で戻せなくなります。移行スクリプトの中で DDL と DML を混ぜると、途中で失敗したときに中途半端な状態が残ります。