はじめに
Laravelを触って2ヶ月の新卒エンジニアがトランザクションについて調べたことをまとめました。本記事は、トランザクションについて全く知識がない人・Laravel初心者を対象としてます。
トランザクションとは?
データベーストランザクションは、複数のデータベース操作が一連の流れとして扱われ、全てが成功するか、全てが失敗する(ロールバックされる)かのどちらかとなる仕組みです。これは一部だけが成功すると、データの整合性が失われる可能性があるため重要です。
下記は処理を図解したものです。
具体例
1. 手動でトランザクションを管理する方法
手動でトランザクションを管理する場合、
1.開始 (beginTransaction())、
2.コミット (commit())
3.ロールバック (rollBack())
を自分で制御します。
try {
DB::beginTransaction();
// データベース操作
// ...
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
// 例外処理
// ...
}
ここでは、try ブロック内で何らかのエラーが発生すると、catch ブロックが実行され、すぐに rollBack() メソッドが呼び出されます。これにより、beginTransaction() から rollBack() までの全てのデータベース操作が無効化されます。
2. クロージャを使用する方法
Laravel の DBファサードのtransactionメソッド(DB::transaction())を使用すると、クロージャ(無名関数)を引数として渡すことができます。この方法を使用すると、トランザクションの開始と終了(成功時のコミットと失敗時のロールバック)を自動的に処理します。
DB::transaction(function () {
// データベース操作
// ...
});
この例では、クロージャ内で例外が発生すると自動的にロールバックされ、例外が発生しなければ自動的にコミットされます。これにより、手動でトランザクション管理をする場合よりも、より簡潔で安全なコードが書けます。
デッドロックの対策
デッドロックとは
デッドロックはトランザクション内で互いに排他的なロックを待つ二つ以上のプロセスが発生すると、データベースのロック状態となります。これは、互いに他のプロセスがロックを解除するのを待つ状態で、永遠に解決しない待機状態が発生します。
手動でトランザクションを管理する方法の場合
トランザクション中に何らかのエラー(デッドロックを含む)が発生した場合、catch ブロックが実行され、DB::rollBack() によりトランザクションがロールバックされます。
クロージャを使用する方法
クロージャを使用してトランザクションを管理する場合、Laravelはトランザクション内で発生する例外を自動的にキャッチしてロールバックします。さらに、再試行回数を指定することで、デッドロックが発生した場合の再試行を容易に行うことができます。このデッドロックを発生させないため›の対策としてタイムアウトやリトライ回数の設定
$retryTimes = 3;
DB::transaction(function () {
// データベース操作
}, $retryTimes);
上記のコードでは、トランザクション内で例外が発生した場合(デッドロックを含む)、Laravelはトランザクションを指定した回数(この場合は3回)再試行します。再試行回数を指定することで、短期的なデッドロック問題を解決することができます。
どちらの書き方がいいか?
細かい制御が必要な場合や、途中でコミットしたい場合は、手動管理が適しています。しかし、より簡潔で安全なクロージャを使用した方法が推奨されます。
まとめ
Laravelのトランザクション処理は要件や使う場所によって、手動管理とクロージャによる自動管理を分けることが良いと思いました。今後もトランザクションの理解を深めていきます!
参考記事