トランザクション
Transaction
トランザクションは、データベースでひとまとまりとして確定・取り消しを扱う処理の単位です。途中の更新だけが確定しない原子性などで整合性を支えます。メール送信や外部APIの操作まで、自動で取り消せるわけではありません。
🐾 猫で例えると?
写真の抱っこのような一連の動きを「ひとまとまり」と見るのが入口です。ただし現実の猫を抱いた時間は巻き戻せません。DBでは、例えばごはんの在庫を減らす処理と給餌記録を追加する処理をセットにし、途中で失敗したら両方の変更を取り消せる点が重要です。
💻 データベース運用における「トランザクション」とは?
トランザクションは、システムにおいてデータの不整合(矛盾)を防ぐための非常に重要な仕組みです。例えば、銀行のシステムで「Aさんの口座から1万円を引き出し、Bさんの口座へ1万円を振り込む」という処理を考えてみましょう。
もし引き出しの直後にシステムエラーが発生し、振り込み処理だけが実行されなかった場合、「1万円がどこかへ消えてしまう」という致命的なバグになります。これを防ぐため、引き出しと振り込みを「トランザクション」という1つの枠でくくり、途中でエラーが起きたら「最初から何もなかったこと(ロールバック)」にするのです。
⚠️ トランザクションの仕組みと注意点
ACIDは、原子性・一貫性・分離性・永続性を表します。原子性は途中の変更だけを確定しない性質です。ただし、業務上のルールまでDBが自動で判断するわけではありません。残高不足の判定や、更新対象が存在するかの確認は別途必要です。
長すぎる処理によるパフォーマンス低下
更新ではロックなどを使って競合を制御しますが、通常の検索まで常に占有するとは限りません。MVCCを使うDBでは過去の行バージョンを読むこともあります。それでも長いトランザクションは待機や後処理の負担を増やすため、人の操作待ちや外部APIへの通信を挟まない設計が基本です。
-- 既存のaccountsテーブルを使う、処理単位の説明用SQL
BEGIN;
UPDATE accounts SET balance = balance - 10000 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 10000 WHERE account_id = 2;
-- アプリ側で対象行数・残高条件・エラーを確認したうえで確定
COMMIT;
-- 確定前に失敗した場合は、代わりにROLLBACKする 更新はトランザクション内で進み、COMMITで確定します。他の接続から何が見えるかはDBと分離レベルによって異なります。この例だけでは残高不足、口座不在、同時送金を十分に扱えないため、そのまま金融処理の完成形として使うことはできません。
🛠️ トランザクションを賢く使うためのポイント
データの整合性を守るトランザクションですが、現場での実装にはいくつかのルールがあります。
- トランザクションの範囲を最小限にする: ロックによる待機時間を減らすため、関係のない検索処理や時間のかかる外部API通信をトランザクションの中に含めないようにします。
- エラーハンドリングを確実に行う: プログラム側で例外(エラー)をキャッチした際、確実に
ROLLBACKが実行されるように設計し、中途半端なデータが残るのを防ぎます。 - 適切な分離レベル(Isolation Level)を選ぶ: パフォーマンスとデータの一貫性のバランスを取るため、要件に合わせてRDBMSのトランザクション分離レベルを調整します。
ひとまとまりの更新を守るのがトランザクションです。正しい制約とエラー処理を組み合わせ、メール送信などDBの外で起こる操作も含めて、どこまで取り消せるかを決めておきましょう。