Kodokon kodokon.com

トランザクション:BEGIN、COMMIT、ROLLBACKとACID

トランザクションで書き込みをアトミックにし、ACIDの保証が日々の実践で何を意味するのかを理解しましょう。

8 分 · 3 問

このレッスンを Kodokon で開く

銀行の振込は二つのUPDATEです。一方の口座から引き落とし、もう一方に入金します。二つ目が失敗すると、データベースは嘘をつくことになります。トランザクションはそのブロックを分割不可能にし、ACIDの保証を与えます。Atomicity(原子性、全か無か)、Consistency(一貫性、制約が有効なまま保たれる)、Isolation(分離性、並行するトランザクションが互いの中途半端な状態を決して見ない)、Durability(永続性、COMMITはクラッシュを生き延びる)。このレッスンでは、本物のsqlite3クライアントを使うのがよいでしょう。一部のウェブのサンドボックスは、クエリごとに自動でコミットしてしまいます。

SQL
CREATE TABLE accounts (
  id INTEGER PRIMARY KEY,
  owner TEXT NOT NULL,
  balance REAL NOT NULL CHECK (balance >= 0)
);

INSERT INTO accounts (owner, balance)
VALUES
  ('alice', 500.0),
  ('bruno', 120.0);
CHECK制約はマイナスの残高を一切禁止する。

通常のケース。BEGINがトランザクションを開き、書き込みが積み重なり、COMMITがそれらを一度に確定します。その間、他のどの接続も中間状態(aliceは引き落とし済み、brunoはまだ入金されていない)を見ることはありません。

SQL
BEGIN;

UPDATE accounts
SET balance = balance - 200
WHERE owner = 'alice';

UPDATE accounts
SET balance = balance + 200
WHERE owner = 'bruno';

COMMIT;

SELECT owner, balance FROM accounts;
アトミックな振込:aliceは300、brunoは320。

今度は失敗のケース。brunoから400を引き落とすとCHECK (balance >= 0)に違反します。その文は失敗します。SQLiteの微妙な点として、デフォルトでは、エラーは問題のある文を取り消しますが、トランザクションは開いたままにします。決めるのはあなたのアプリケーションコードの役目です。ROLLBACKですべてを取り消す(まっとうな反射)か、エラーが回復可能なら続行するか。

SQL
BEGIN;

UPDATE accounts
SET balance = balance - 400
WHERE owner = 'bruno';
-- Error: CHECK (balance >= 0) rejects the row.

ROLLBACK;

SELECT owner, balance FROM accounts;
ROLLBACKのあと、最後のCOMMIT時点の残高は無傷のまま。

理解度チェック

このレッスンの要点をしっかり覚えているか確認しましょう。

  1. ACIDのAである原子性は何を保証しますか?
    • トランザクションのクエリが速く実行される
    • トランザクションが全部適用されるか、まったく適用されないか
    • 二つのトランザクションが同じテーブルを決して読めない
  2. ROLLBACKのあと、データベースはどの状態にありますか?
    • 最後のCOMMIT時点の状態。まるでBEGINがなかったかのように
    • エラーの前に成功したUPDATEは保たれる
    • データベースは次のBEGINまでロックされたまま
  3. なぜ10,000件のINSERTは一つのSQLiteトランザクションの中だとずっと速いのですか?
    • SQLiteが挿入されたデータを一括で圧縮するから
    • 文ごとではなくCOMMITで一度だけディスク同期するから
    • トランザクションの間はインデックスが無効になるから