PostgreSQL: PostgreSQLのトランザクションと同時実行制御

最終更新:2026-08-26

1. 学習目標


2. ストーリー

AliceはECプラットフォームの注文システムを担当しています。プロモーション中、限定商品の在庫が残り1点しかない状態で、2人のユーザーがほぼ同時に注文しました。

トランザクション保護がないと、両方の注文が成功し、在庫が-1になって売り越しが発生する可能性があります。AliceはPostgreSQLのトランザクション分離、MVCC、行ロック機構を使って、1人のユーザーのみが在庫減算に成功し、もう1人はブロックされるかエラーになるようにして、データの一貫性を保証する必要があります。


3. 概念: ACIDとトランザクションの基本

(1) ACIDの4つの特性

特性 英語 意味 PostgreSQLの実装
原子性 Atomicity トランザクションはオールオアナッシング。完全に成功するか完全にロールバック WAL(Write-Ahead Log)
一貫性 Consistency トランザクション前後でデータベースが制約を満たす 制約、トリガー、型チェック
分離性 Isolation 同時実行トランザクションが互いに干渉しない MVCC + ロック
永続性 Durability コミットされたデータは失われない WAL + fsync

▶ サンプル: ACID原子性——振込は完全に成功するか完全にロールバック

SQL
BEGIN;
UPDATE accounts SET balance = balance - 500 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 500 WHERE account_id = 2;
COMMIT;

Output:

TEXT 📖 参照専用
-- SQL statement executed successfully

(2) トランザクション制御文

目的 対応するSQL
トランザクション開始 明示的にトランザクションを開く BEGINまたはSTART TRANSACTION
トランザクションコミット すべての変更を永続化 COMMIT
トランザクションロールバック すべての変更を取り消し ROLLBACK

▶ サンプル: トランザクションをロールバックしてデータを復元

SQL
BEGIN;
DELETE FROM orders WHERE order_id = 999;
-- おっと、誤削除!
ROLLBACK;
-- データが復元される

Output:

TEXT 📖 参照専用
-- SQL statement executed successfully

(3) 自動コミットモード

PostgreSQLはデフォルトで自動コミットが有効です。各SQL文は自動的に独自のトランザクションとしてコミットされます。

モード 動作 psql設定
自動コミットON 各文が自動コミット デフォルト
自動コミットOFF 手動COMMITが必要 \set AUTOCOMMIT off

▶ サンプル: 自動コミットを無効にして手動コミット

BASH
\set AUTOCOMMIT off
DELETE FROM orders WHERE order_status = 'cancelled';
-- コミット前に確認
SELECT count(*) FROM orders WHERE order_status = 'cancelled';
COMMIT;

Output:

TEXT 📖 参照専用
# command executed successfully

(4) SAVEPOINTによる部分ロールバック

トランザクション内にセーブポイントを設定し、トランザクション全体ではなくそのセーブポイントまでロールバックできます。

▶ サンプル: SAVEPOINTで部分ロールバック

SQL
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
SAVEPOINT sp1;
UPDATE accounts SET balance = balance - 200 WHERE account_id = 1;
-- 2回目の更新が間違っていた、セーブポイントまでロールバック
ROLLBACK TO SAVEPOINT sp1;
-- 1回目の更新は有効なまま、コミット可能
COMMIT;

Output:

TEXT 📖 参照専用
-- SQL statement executed successfully
目的
SAVEPOINT sp_name セーブポイントを作成
ROLLBACK TO SAVEPOINT sp_name セーブポイントまでロールバック
RELEASE SAVEPOINT sp_name セーブポイントを解放(以降のロールバックは参照不可)

4. 概念: トランザクション分離レベル

(1) PostgreSQLの3つの分離レベル

PostgreSQLは3つの分離レベルのみを実装しています(READ UNCOMMITTEDはREAD COMMITTEDにマッピングされます)。

分離レベル ダーティリード 非再現リード ファントムリード PGの実装
READ COMMITTED 不可 可能性あり 可能性あり デフォルトレベル。各クエリは最新のコミット済みスナップショットを参照
REPEATABLE READ 不可 不可 可能性あり(PGは実際にファントムを防止) トランザクション開始時のスナップショットを参照
SERIALIZABLE 不可 不可 不可 最も厳格。直列化矛盾を検出

▶ サンプル: 分離レベルの設定

SQL
BEGIN ISOLATION LEVEL READ COMMITTED;
-- または
BEGIN ISOLATION LEVEL REPEATABLE READ;
-- または
BEGIN ISOLATION LEVEL SERIALIZABLE;

Output:

TEXT 📖 参照専用
-- SQL statement executed successfully

(2) READ COMMITTED と REPEATABLE READ の実践

▶ サンプル: READ COMMITTED——同一トランザクショ��内の2回のクエリが異なる結果を返す

SQL
-- セッション1
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT balance FROM accounts WHERE account_id = 1; -- 1000が返る

-- セッション2(別の接続)
UPDATE accounts SET balance = 800 WHERE account_id = 1;
COMMIT;

-- セッション1に戻る
SELECT balance FROM accounts WHERE account_id = 1; -- 800が返る(コミット済みの変更を参照)
COMMIT;

Output:

TEXT 📖 参照専用
 count 
-------
     5
(1 row)

▶ サンプル: REPEATABLE READ——同一トランザクション内の2回のクエリが一貫した結果を返す

SQL
-- セッション1
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT balance FROM accounts WHERE account_id = 1; -- 1000が返る

-- セッション2(別の接続)
UPDATE accounts SET balance = 800 WHERE account_id = 1;
COMMIT;

-- セッション1に戻る
SELECT balance FROM accounts WHERE account_id = 1; -- 依然として1000が返る
COMMIT;

Output:

TEXT 📖 参照専用
 count 
-------
     5
(1 row)

(3) SERIALIZABLE分離レベル

SERIALIZABLEは最も厳格なレベルです。PostgreSQLはSerializable Snapshot Isolation(SSI)を使用して直列化矛盾を検出します。

▶ サンプル: SERIALIZABLEの矛盾検出

SQL
-- セッション1
BEGIN ISOLATION LEVEL SERIALIZABLE;
SELECT balance FROM accounts WHERE account_id = 1;

-- セッション2
BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;

-- セッション1に戻る
UPDATE accounts SET balance = balance + 50 WHERE account_id = 1;
-- ERROR: 同時更新によりアクセスを直列化できませんでした

Output:

TEXT 📖 参照専用
 count 
-------
     5
(1 row)
分離レベル選択 シナリオ 推奨レベル
ほとんどのWebアプリ READ COMMITTED デフォルト、パフォーマンスと一貫性のバランス
一貫したスナップショットが必要なレポート/監査 REPEATABLE READ 同一トランザクションの読み取りが一貫
銀行/金融の中核トランザクション SERIALIZABLE 最も厳格、安全性と引き換えにパフォーマンス

5. 概念: MVCCマルチバージョン同時実行制御

(1) MVCCの核心原理

MVCC(Multi-Version Concurrency Control)はPostgreSQLの同時実行制御の中核です。各トランザクションはある時点のデータのスナップショットを参照します。読み取りは書き込みをブロックせず、書き込みは読み取りをブロックしません。

各行バージョン(タ��ル)には4つの隠しフィールドが含まれます。

フィールド 意味
xmin その行を挿入したトランザクションID
xmax その行を削除/更新したトランザクションID(0はまだ有効)
xmin可視性 トランザクションIDが現在のスナップショットより小さいとこの行を参照可能
xmax可視性 トランザクションIDが現在のスナップショット以上だとこの削除を参照不可

▶ サンプル: 行のバージョン情報を表示

SQL
SELECT xmin, xmax, * FROM products WHERE product_id = 1;

Output:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

(2) MVCC読み取り/書き込み同時実行フロー

100%
sequenceDiagram
    participant R as リーダー(Tx1)
    participant W as ライター(Tx2)
    participant T as テーブル

    R->>T: SELECT(Tx1開始時のスナップショット)
    T-->>R: バージョンV1を返す(xmin=100, xmax=0)
    W->>T: UPDATE(新バージョン作成)
    T-->>T: V1 xmax=200, V2 xmin=200 xmax=0
    R->>T: 再度SELECT(同じスナップショット)
    T-->>R: 依然V1を返す(Tx2未コミット)
    W->>T: COMMIT
    Note over T: V2が新しいトランザクションから可視に
    R->>T: 再度SELECT(同じスナップショット)
    T-->>R: 依然V1を返す(REPEATABLE READ)

▶ サンプル: MVCC読み取りは書き込みをブロックしない

SQL
-- セッション1: 長時間実行される読み取り
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT * FROM orders WHERE order_date = '2025-01-01';
-- このクエリはライターをブロックしない

-- セッション2: 同時書き込み(セッション1からのブロックなし)
UPDATE orders SET order_status = 'shipped' WHERE order_id = 100;
COMMIT;

Output:

TEXT 📖 参照専用
UPDATE 3

(3) MVCCのコスト: デッドタプルとVACUUM

MVCCでのUPDATEは元の行を変更せず、新しいバージョンを作成します。古い行はデッドタプルとなり、VACUUMによるクリーンアッ��が必要です。

操作 MVCCの動作 デッドタプル
INSERT 新規行を作成 なし
DELETE 古い行のxmaxをマーク 1デッドタプル生成
UPDATE 古い行のxmaxをマーク+新規行を作成 1デッドタプル生成

▶ サンプル: デッドタプル数の表示

SQL
SELECT relname, n_live_tup, n_dead_tup, last_vacuum, last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;

Output:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

▶ サンプル: 手動VACUUM実行

SQL
VACUUM orders;              -- 領域を再利用、読み取りはブロックしない
VACUUM FULL orders;         -- 全テーブル書き換え、テーブルをロック
VACUUM ANALYZE orders;      -- 再利用+統計情報更新

Output:

TEXT 📖 参照専用
-- SQL statement executed successfully
VACUUM種別 ロックレベル 領域再利用 速度
VACUUM SHARE 再利用可能としてマーク、ディスクは戻さない 高速
VACUUM FULL ACCESS EXCLUSIVE ディスクを完全に戻す 低速、テーブルロック
VACUUM ANALYZE SHARE VACUUMと同じ+統計情報更新 高速

6. 概念: ロック

(1) 行レベルロック

PostgreSQLは行を変更する際に自動的に行ロックを取得します。他のトランザクションは待機する必要があります。

行ロック種別 取得方法 競合
FOR UPDATE SELECT ... FOR UPDATE 排他ロック、他のFOR UPDATE / FOR NO KEY UPDATEをブロック
FOR NO KEY UPDATE SELECT ... FOR NO KEY UPDATE FOR KEY SHAREをブロックしない
FOR SHARE SELECT ... FOR SHARE FOR UPDATE / FOR NO KEY UPDATEをブロック
FOR KEY SHARE SELECT ... FOR KEY SHARE 最も弱く、FOR UPDATEのみブロック

▶ サンプル: SELECT FOR UPDATEで売り越し防止

SQL
BEGIN;
SELECT stock FROM products WHERE product_id = 1 FOR UPDATE;
-- stock = 1、行がロックされる
UPDATE products SET stock = stock - 1 WHERE product_id = 1;
COMMIT;
-- 別のトランザクションが同じ行でFOR UPDATEを試みると待機

Output:

TEXT 📖 参照専用
UPDATE 3

(2) テーブルレベルロック

ロックモード 取得方法 競合
ACCESS SHARE SELECT ACCESS EXCLUSIVEと競合
ROW SHARE SELECT FOR EXCLUSIVE / ACCESS EXCLUSIVEと競合
ROW EXCLUSIVE UPDATE/DELETE SHARE / EXCLUSIVEなどと競合
SHARE LOCK TABLE ... SHARE ROW EXCLUSIVEなどと競合
ACCESS EXCLUSIVE ALTER TABLE すべてのロックと競合

▶ サンプル: 明示的なテーブルロック

SQL
LOCK TABLE orders IN ACCESS EXCLUSIVE MODE;
-- 重要な操作を実行
COMMIT;

Output:

TEXT 📖 参照専用
-- SQL statement executed successfully

(3) Advisory Lock(PostgreSQL固有)

Advisory Lockはテーブル行に紐付かないアプリケーションレベルのロックで、分散コーディネーションに適��ています。

関数 機能
pg_try_advisory_lock(id) ノンブロッキング。失敗時は即座にfalseを返す
pg_advisory_lock(id) 取得までブロック待機
pg_advisory_unlock(id) ロックを解放
pg_advisory_xact_lock(id) トランザクション終了時に自動解放

▶ サンプル: Advisory Lockで重複処��防止

SQL
-- ノンブロッキング試行
SELECT pg_try_advisory_lock(12345);
-- trueが返る: ロック取得、処理続行
-- falseが返る: 別セッションが保持、スキップ

-- トランザクションレベルのAdvisory Lock(COMMIT/ROLLBACKで自動解放)
BEGIN;
SELECT pg_advisory_xact_lock(12345);
-- 処理を実行...
COMMIT; -- ロック自動解放

Output:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

▶ サンプル: 単一インスタンスジョブ用のAdvisory Lock

SQL
CREATE FUNCTION run_daily_report() RETURNS void AS $$
BEGIN
  IF pg_try_advisory_lock(99999) THEN
    -- 一度に1つのセッションのみがこれを実行可能
    INSERT INTO report_log (report_date, status)
    VALUES (CURRENT_DATE, 'running');
    PERFORM pg_sleep(5); -- 処理をシミュレート
    UPDATE report_log SET status = 'done' WHERE report_date = CURRENT_DATE;
    PERFORM pg_advisory_unlock(99999);
  ELSE
    RAISE NOTICE 'レポートは既に別のセッションで実行中です';
  END IF;
END;
$$ LANGUAGE plpgsql;

Output:

TEXT 📖 参照専用
INSERT 0 1

7. 概念: デッドロックの検出と対処

(1) デッドロックの発生原因

2つのトランザクションが互いに相手の保持するロックを待機し、循環依存を形成します。

100%
flowchart LR
    A[Tx1: 行Aをロック] -->|行Bを待機| B[Tx2: 行Bをロック]
    B -->|行Aを待機| A
    A -->|デッドロック!| C[PGが1秒で自動検出]

▶ サンプル: デッドロックシナリオ

SQL
-- セッション1
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1; -- 行1をロック

-- セッション2
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 2; -- 行2をロック

-- セッション1(行2を待機)
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;

-- セッション2(デッドロック!)
UPDATE accounts SET balance = balance + 100 WHERE account_id = 1;
-- ERROR: デッドロックが検出されました

Output:

TEXT 📖 参照専用
-- SQL statement executed successfully

(2) デッドロックの検出と防止

戦略 説明
自動検出 PostgreSQLはデフォルトで1秒ごとにチェック(deadlock_timeout
自動ロールバック デッドロック検出時に1つのトランザクションを自動ロールバック
固定ロック順序 常に同じ順序でロックを取得し、循環待機を回避
短いトランザクション トランザクションがロックを保持する時間を短縮

▶ サンプル: 固定ロック順序でデッドロック防止

SQL
-- 常にaccount_id順で行をロック
BEGIN;
SELECT * FROM accounts WHERE account_id IN (1, 2) ORDER BY account_id FOR UPDATE;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
COMMIT;

Output:

TEXT 📖 参照専用
 count 
-------
     5
(1 row)

8. 概念: 悲観的ロックと楽観的ロック

(1) 2つの同時実行戦略の比較

観点 悲観的ロック 楽観的ロック
考え方 先にロック、後に変更。競合時はブロック 先に変更、後に確認。競合時はリトライ
実装 SELECT FOR UPDATE versionカラム + WHERE version = old
競合頻度 高競合シナリオに適する 低競合シナリオに適する
パフォーマンス ロック待機オーバーヘッド リトライオーバーヘッド
デッドロックリスク あり なし

▶ サンプル: 悲観的ロックによる在庫減算

SQL
BEGIN;
SELECT stock FROM products WHERE product_id = 1 FOR UPDATE;
-- stock = 1、行がロックされる
IF stock > 0 THEN
  UPDATE products SET stock = stock - 1 WHERE product_id = 1;
END IF;
COMMIT;

Output:

TEXT 📖 参照専用
UPDATE 3

▶ サンプル: 楽観的ロックによる在庫減算

SQL
-- versionカラムを追加
ALTER TABLE products ADD COLUMN version INT DEFAULT 1;

-- 楽観的更新
UPDATE products
SET stock = stock - 1, version = version + 1
WHERE product_id = 1 AND version = 5;
-- 影響行数 = 0なら、誰かが変更しているのでリトライが必要

Output:

TEXT 📖 参照専用
-- SQL statement executed successfully

▶ サンプル: 楽観的ロックのアプリケーション層リトライロジック

SQL
CREATE FUNCTION deduct_stock(p_id INT, p_qty INT) RETURNS BOOLEAN AS $$
DECLARE
  v_version INT;
  v_stock INT;
  v_updated INT;
BEGIN
  LOOP
    SELECT stock, version INTO v_stock, v_version
    FROM products WHERE product_id = p_id;

    IF v_stock < p_qty THEN RETURN FALSE; END IF;

    UPDATE products
    SET stock = stock - p_qty, version = version + 1
    WHERE product_id = p_id AND version = v_version;

    GET DIAGNOSTICS v_updated = ROW_COUNT;
    IF v_updated > 0 THEN RETURN TRUE; END IF;
    -- 競合、リトライ
  END LOOP;
END;
$$ LANGUAGE plpgsql;

Output:

TEXT 📖 参照専用
 count 
-------
     5
(1 row)

9. 概念: 二相コミット(2PC)

(1) 分散トランザクションのための2PC

トランザクションが複数のデータベースや外部リソースにまたがる場合、原子性を保証するために二相コミットが必要です。

フェーズ 操作 説明
PREPARE PREPARE TRANSACTION 'tx_id' トランザクションが準備状態に入り、WALに書き込み
COMMIT COMMIT PREPARED 'tx_id' フェーズ2でコミット確認
ROLLBACK ROLLBACK PREPARED 'tx_id' フェーズ2でロールバック確認

▶ サンプル: 二相コミットフロー

SQL
-- フェーズ1: 準備
BEGIN;
UPDATE accounts SET balance = balance - 500 WHERE account_id = 1;
PREPARE TRANSACTION 'transfer_out';

-- (コーディネーターが全参加者の準備完了を確認)

-- フェーズ2: コミット
COMMIT PREPARED 'transfer_out';

Output:

TEXT 📖 参照専用
-- SQL statement executed successfully

▶ サンプル: 準備済みトランザクションの表示

SQL
SELECT * FROM pg_prepared_xacts;

Output:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)
パラメータ デフォルト 説明
max_prepared_transactions 0 2PCを使用するには > 0が必要
deadlock_timeout 1s デッドロック検出間隔
idle_in_transaction_session_timeout 0 トランザクションアイドルタイムアウト(ミリ秒)

10. 実践: ECサイトの完全な在庫減算トランザクション

Aliceは同時注文を処理し、売り越しを防止し、注文をログ記録する完全な在庫減算トランザクションを必要としています。

SQL
-- Step 1: テーブル作成
CREATE TABLE products (
  product_id INT PRIMARY KEY,
  product_name TEXT NOT NULL,
  stock INT NOT NULL DEFAULT 0,
  price NUMERIC(10,2) NOT NULL,
  version INT NOT NULL DEFAULT 1
);

CREATE TABLE orders (
  order_id SERIAL PRIMARY KEY,
  customer_id INT NOT NULL,
  product_id INT NOT NULL,
  quantity INT NOT NULL,
  total_price NUMERIC(10,2) NOT NULL,
  order_status TEXT DEFAULT 'pending',
  created_at TIMESTAMP DEFAULT now()
);

INSERT INTO products VALUES
  (1, '限定版腕時計', 1, 299.99, 1),
  (2, 'ワイヤレスヘッドフォン', 50, 89.99, 1);

-- Step 2: 人気商品向け悲観的ロックアプローチ
CREATE FUNCTION place_order(
  p_customer_id INT, p_product_id INT, p_qty INT
) RETURNS INT AS $$
DECLARE
  v_stock INT;
  v_order_id INT;
BEGIN
  BEGIN
    -- 商品行をロック
    SELECT stock INTO v_stock
    FROM products
    WHERE product_id = p_product_id
    FOR UPDATE;

    IF v_stock < p_qty THEN
      RAISE EXCEPTION '在庫不足: % < %', v_stock, p_qty;
    END IF;

    -- 在庫減算
    UPDATE products
    SET stock = stock - p_qty
    WHERE product_id = p_product_id;

    -- 注文作成
    INSERT INTO orders (customer_id, product_id, quantity, total_price)
    VALUES (p_customer_id, p_product_id, p_qty,
            p_qty * (SELECT price FROM products WHERE product_id = p_product_id))
    RETURNING order_id INTO v_order_id;

    RETURN v_order_id;
  EXCEPTION
    WHEN OTHERS THEN
      RAISE NOTICE '%', SQLERRM;
      RETURN -1;
  END;
END;
$$ LANGUAGE plpgsql;

-- Step 3: 同時注文のテスト
SELECT place_order(101, 1, 1); -- 成功、注文作成
SELECT place_order(102, 1, 1); -- 失敗、在庫不足

-- Step 4: 結果確認
SELECT product_id, stock FROM products WHERE product_id = 1;
SELECT order_id, customer_id, order_status FROM orders;

❓ よくある質問

Q PostgreSQLにREAD UNCOMMITTED分離レベルがないのはなぜですか?
A PostgreSQLはREAD UNCOMMITTEDをREAD COMMITTEDにマッピングします。MVCCアーキテクチャでは未コミットデータの読み取りが不可能であり(常にスナップショットを読み取る)、PGではダーティリードが発生しないためです。
Q REPEATABLE READはファントムリードを防止できますか?
A PostgreSQLのREPEATABLE READは実際にファントムリードを防止できます。SQL標準がそのレベルに要求する以上のことです。標準ではSERIALIZABLEのみがファントムを防止しますが、PGのMVCCスナップショット機構はREPEATABLE READでもファントムをブロックします。
Q SAVEPOINTはネストできますか?
A はい。SAVEPOINTはネストをサポートしています。内部のROLLBACK TO SAVEPOINTはそのセーブポイントまでのみロールバックし、外側には影響しません。RELEASE SAVEPOINTは指定されたセーブポイントとそれ以降に作成されたすべてのセーブポイントを解放します。
Q SELECT FOR UPDATEと直接UPDATEの違いは?
A SELECT FOR UPDATEはまず行をロックしてから変更するか判断します。「読み取ってから書き込む」複合操作に最適です。直接UPDATEは1ステップで全て行います。SELECT FOR UPDATEの利点は変更前にビジネス判断ができることです。
Q VACUUM FULLを使うべき時は?
A テーブルが大幅に肥大化し、通常のVACUUMでは領域を再利用できない場合のみです。VACUUM FULLはACCESS EXCLUSIVEロックが必要で、その間テーブルは完全に利用不可になります。通常のメンテナンスはautovacuumに任せるべきです。
Q Advisory Lockと通常ロックの違いは?
A Advisory Lockはアプリケーション定義で、テーブルや行に紐付かず、トランザクション終了時に(xactバージョンを使用しない限り)自動解放されません。通常ロックはPostgreSQLが自動管理し、特定のデータベースオブジェクトに紐付きます。
Q 2PCはいつ使用しますか?
A 2PCは主にデータベースやシステムをまたぐ分散トランザクションに使用し、コーディネーターが統一コミットする必要があります。単一データベーストランザクションは通常のBEGIN/COMMITを使用します。2PCには追加オーバーヘッドがあり、max_prepared_transactionsの設定が必要です。

📖 まとめ


📝 練習問題

  1. accountsテーブルで口座1から口座2へ200 USDを振り込み、口座1の残高が不足する場合はロールバックするトラ��ザクションを書いてください。

  2. ⭐⭐ SAVEPOINTを使用してトランザクションを書いてください。まず注文レコードを挿入し、次に在庫を更新しようと試み、在庫が不足する場合はセーブポイントまでロールバックして注文を保持(ステータスを'failed'にマーク)し、トランザクションをコミットします。

  3. ⭐⭐⭐ 楽観的ロック版の在庫減算を実装してください。versionカラムを使用し、同時実行競合時に最大3回自動リトライし、3回すべて失敗したらFALSEを返します。各リトライをretry_logテーブルにログ記録してください。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%