1.ダーティリード(Dirty Read)
トランザクション処理の最中にストレージに書き込んだ未確定なデータを、
別のトランザクションが読み込んで使用してしまう
対応策
トランザクション分離レベル “READ COMMITTED” が必要になる
トランザクションの処理中によって変更されたデータは
そのトランザクションが終了するまで他のトランザクションから読み
取れないようロックするなどの対策が要求される
2.デッドロック(Dead Lock)
お互いがロック解除待ち状態となり、どちらも処理が進行できなくなる状態
まずはじめに「資源」をロックする
ロックされた「資源」はロックが解除されるまで、
他のタスク(処理)から操作することができなくなるので
処理が終わったらロックを解除する
解除しないと、互いにロックが解除されるのを待ち続けてしまうあ
この状態をデッドロックという
デッドロックが発生すると処理がとまってしまう
対応策
デッドロックを回避するには
資源のロック順序を両方のタスクで揃える
不必要にロックを掛けない
可能な限り行ロックを採用する(全体ではなく、行部分のみロック)
3.ノンリピータブルリード(Non-Repeatable Read)
同じレコードを何度も繰り返し読み出す処理の場合
途中で他のトランザクションが内容を更新してしまう
対応策
トランザクション分離レベル “REPEATABLE READ” が必要になるので
2番目に高いレベルであり、その分だけ性能は低くなる
処理の特性やコスト等の兼ね合いで完全に防ぐ必要があるか判断する
4.ロストアップデート(Lost Update)
複数のユーザーが同時にアクセスしたときに起こる
更新したはずのデータが「失われてしまう」状態
対応策
楽観ロック(Optimistic Locking)
同時アクセスはあまり起きないだろうという楽観的なロック
データの同時更新が低い場合に適している
読み取り操作が多く、書き込み操作が少ない場合に適している
SNS等でユーザーが投稿やコメントを編集する場合に有効
各データレコードにバージョン番号やタイムスタンプを付与する
データがいつ更新されたかを追跡できる
データを更新する際には事前にバージョン番号等を読み込む
更新する際にもう一度バージョン番号等を読み込む
バージョン番号等が変わっていれば、競合があったと見なす
競合が検出された場合、通常は更新を拒否し
最新のデータに基づいて再度更新処理を行う
利点
データ全体へのロックではないため
システムのスループットが向上する
欠点
競合が頻繁に発生する環境では再試行が増え効率が低下する
悲観ロック (Pessimistic locking)
同時アクセスが頻繁に起きるだろうという悲観的なロック
同じデータに対し同時更新の可能性が高い場合に適している
データの整合性が非常に重要で、
競合によるリスクを最小限に抑えたい場合に適している
処理時間が長時間の場合はデータ保護のためには有効
書き込み操作が頻繁に行われる場合に適している
銀行の口座システムでの資金移動に有効
データにアクセスし、他からのアクセスができないようにロックする
ロックはデータの更新が完了するまで継続する
悲観ロックはデータの整合性を保護するために使用される
利点
データの整合性と安全性が強化されデータの品質が向上する
欠点
ロックによって全体のスループットが低下する
5.補足
トランザクション分離レベルについて
どの程度の水準で両立(妥協)するかを表す
最も独立性の高い「SERIALIZABLE」から
最も低い「READ UNCOMMITED」まで4段階が定義されている
SERIALIZABLE
最も独立性(安全性)が高い分離レベル
トランザクションを順番に実行(直列化)したのと同じ様に、
他のトランザクションのデータ更新の影響をまったく受けない
最も性能は低い
アクセスが競合すると「先客」の終了を待たなければならない
REPEATABLE READ
2番目に独立性が高い分離レベル
何度繰り返し対象データを読み取っても
途中で他のトランザクションによって変更されないので
同じ値が返ってくる
他のトランザクションによるデータ更新の影響を受ける
READ COMMITED
3番目に独立性が高い分離レベル
他のトランザクションがコミットしたデータのみを読み取る
何度も同じデータを読み込むと
他のトランザクションによる更新で値が変わってしまう
多くの著名RDBMSのデフォルトの分離レベル
READ UNCOMMITED
最も独立性が低い分離レベル
他のトランザクションが引き起こすあらゆる影響を受ける
処理途上や不完全な状態のデータを読み込んでしまう
最も高速に動作する
処理を妨げるロックなどは最小限に抑えられるため