MariaDB講座 / COMMIT・ROLLBACKでInnoDBトランザクションを扱う

トランザクションは、関連する変更を一つの確定単位へまとめます。InnoDBではSTART TRANSACTIONで開始し、結果が正しければCOMMIT、取消ならROLLBACKで終えます。すべて同じ接続の未確定な範囲に置く必要があります。

一緒に成功すべき変更を定める

ここでは二つの残高の片側だけが変わらないようにします。トランザクションの小さな実習で、完全な決済実装ではありません。実決済には台帳、二重処理防止、認可と監査も必要です。対象の確認、同時実行、呼び出し側が制御するルーチンにも同じ境界が関係します。

COMMIT後の変更は後の通常ROLLBACKでは取り消せません。autocommitだけで現在トランザクション中かを断定せず、@@in_transactionも確認します。既存トランザクションの途中で不用意に開始し直さないでください。

独立したInnoDB表を準備する

course_userでshopcourseを確認し、accounts_35_labがないか調べます。必要な既存行は保存し、セットアップを実験前に完了します。

USE shopcourse;
CREATE TABLE accounts_35_lab (
 account_id INT PRIMARY KEY,balance DECIMAL(12,2) NOT NULL CHECK(balance>=0)
) ENGINE=InnoDB;
INSERT INTO accounts_35_lab VALUES(1,10000),(2,5000);
SELECT @@autocommit,@@in_transaction;
SHOW CREATE TABLE accounts_35_lab;

ID1が10000、2が5000、ENGINE=InnoDBか確認します。接続を切り替えたり一文ずつ確定したりするツールでは、この手順の意味が変わります。

引き落とし件数を確認してから続ける

同じ接続で順に実行します。

START TRANSACTION;
SELECT account_id,balance FROM accounts_35_lab
WHERE account_id IN (1,2) ORDER BY account_id FOR UPDATE;
UPDATE accounts_35_lab SET balance=balance-3000
WHERE account_id=1 AND balance>=3000;
SELECT ROW_COUNT() AS debit_rows;

debit_rowsが1の場合だけ次へ進みます。0やエラーなら、この接続でROLLBACKして止め、次の加算を実行しません。

UPDATE accounts_35_lab SET balance=balance+3000 WHERE account_id=2;
SELECT ROW_COUNT() AS credit_rows;

credit_rowsも1が必要です。違えばROLLBACKして終了します。両方成功した場合だけ残高を読みます。

SELECT account_id,balance FROM accounts_35_lab ORDER BY account_id;

初期状態からなら7000と8000です。SQL例は人が監督する手順で、実プログラムには件数や例外に対する明示的な分岐が必要です。

取り消しと確定を別々に観察する

一回目は取消します。

ROLLBACK;
SELECT account_id,balance FROM accounts_35_lab ORDER BY account_id;

10000と5000へ戻ります。戻らなければ接続や途中COMMITを調べます。次に引き落とし・加算・各件数確認を再実行し、正しい場合だけ今回はROLLBACKの代わりに次を選びます。

COMMIT;
SELECT account_id,balance FROM accounts_35_lab ORDER BY account_id;

確定後は7000と8000で、後から通常ROLLBACKしても初期値には戻りません。反対額を加えることが一般的な完全復旧とは限りません。

エラーとDDLの境界を理解する

一文のエラーが常に全体を自動取消するわけではありません。エラー種別とドライバーの扱いを確認し、ロックは短く保ちます。待機中のまま長い利用者入力を待たないようにします。

CREATE、ALTER、DROP、TRUNCATEなどは暗黙確定や特別な境界があります。非トランザクションエンジン、外部APIやファイル転送もDBのROLLBACKだけでは取消できません。途中で止まった場合は該当接続でROLLBACKし、@@in_transactionを確認します。必要データがない実習表だけを、実験外で削除します。

ROLLBACK;
DROP TABLE shopcourse.accounts_35_lab;

関連する公式リファレンス