テックカリキュラム

トランザクションと排他制御の基本と実践

トランザクションと排他制御の基本と実践

データベースを安全かつ効率的に操作するためには、トランザクション制御排他制御(ロック)の理解が不可欠です。本章では、BEGIN / COMMIT / ROLLBACK を用いたトランザクションの基本操作から、ロックの種類とデッドロックの回避分離レベルの違いまで、現場で活用できる知識を詳しく解説します。

トランザクションの基本操作

トランザクションとは、データベースにおける一連の処理を「1つのまとまり」として扱う仕組みです。成功すればすべてが反映され、失敗すればすべて取り消されます。これはACID特性(Atomicity, Consistency, Isolation, Durability)に基づいています。

基本的な構文

BEGIN; UPDATE accounts SET balance = balance - 10000 WHERE id = 1; UPDATE accounts SET balance = balance + 10000 WHERE id = 2; COMMIT; -- 正常終了で確定 

もし途中でエラーが起きた場合は、次のように取り消します:

ROLLBACK; -- エラー時に元に戻す 

これにより、整合性のあるデータ更新が可能になります。

ロックモードとデッドロック

ロックの種類

同時実行制御のため、DBは裏側で様々なロックを行います。主な種類は以下の通りです。

ロックモード説明用途
共有ロック(Shared Lock)読み取りを許可、書き込みはブロックSELECT FOR SHARE
排他ロック(Exclusive Lock)書き込みのための完全ロックSELECT FOR UPDATE
行ロック特定行のみをロック通常のUPDATE操作など
テーブルロックテーブル全体をロック大量更新時やDDL操作

デッドロックとは?

デッドロックは、複数のトランザクションが互いに相手のロック解除を待ち続け、処理が永遠に進まない状態です。

-- トランザクションA BEGIN; UPDATE users SET name = 'A' WHERE id = 1; -- トランザクションB BEGIN; UPDATE users SET name = 'B' WHERE id = 2; -- Aがid=2、Bがid=1を更新しようとするとデッドロック 

デッドロックの回避策

  • 更新順序を統一する(ID順など)
  • トランザクションを短く保つ
  • リトライ処理を組み込む(アプリ側で再試行)

PostgreSQLやMySQLでは、デッドロックが検出されると自動的に一方のトランザクションが強制終了されます。そのため、アプリ側でリトライ処理を設けることが重要です。

トランザクション分離レベルの理解

複数のトランザクションが同時に走っている場合、どの程度まで互いの影響を受けるかを定めたものが分離レベル(Isolation Level)です。

分離レベル読み取りの一貫性防止される問題
Read Uncommitted未コミットのデータを読めるなし(最も危険)
Read Committedコミット済のみ読めるダーティリード防止
Repeatable Read読み取り一貫性を保証ダーティリード、ファジーリード防止
Serializable完全に直列化された処理すべての同時実行問題を防止

PostgreSQLのデフォルトは Read Committed です。

-- 分離レベルを変更する例(PostgreSQL) SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; BEGIN; -- トランザクション処理 COMMIT; 

レベルが高いほど安全性は増しますが、同時実行性能が低下するため、用途に応じて使い分けることが重要です。

まとめ

トランザクションと排他制御は、データの整合性を保ち、信頼性の高いアプリケーションを作るための基盤です。

  • BEGIN / COMMIT / ROLLBACK を活用して確実なデータ操作を行う
  • ロックとデッドロックの動作を理解して、安全な並行処理を設計する
  • 分離レベルはパフォーマンスと整合性のトレードオフを理解して選択する

これらの知識を身につけることで、複雑なシステムでも安定性と性能を両立できるようになります。