トランザクション
トランザクションを利用すれば、ある種の潜在的なエラーの状況や並行性の問題はデータベースが面倒を見てくれるので、アプリケーションはそれらを気にしなくてよくなります (このことは 安全性の保証 と呼ばれます)
トランザクションが提供する安全性の保証は、ACID で示される。ACID は原子性 (Atomicity)、一貫性 (Consistency)、分離性 (Isolation)、永続性 (Durability) を意味する。
原子性
エラーの際にトランザクションを中断し、そのトランザクションのすべての書き込みを破棄できることが、ACID の原子性を決定づける特徴。
一貫性
データベースが「良い状態」にあることを示すアプリケーション固有の概念。
この一貫性を保つことはデータベースだけの責任ではなく、アプリケーションの特性であるので、C (Consistency) は実際には ACID には属さない。
分離性
ACID における分離性とは、並行して実行されたトランザクションがお互いから分離されており、お互いのつま先を踏みつけあうようなことがないという意味。分離性を直列化可能性 (serializability) ともいう。直列化可能性とは、それぞれのトランザクションがデータベース全体の中で実行されている唯一のトランザクションであるかのように振る舞うことを意味する。データベースは、複数のトランザクションが実際には並列に実行されていたとしても、それらがコミットされた後の結果はそれぞれが順番に (serially) (1つずつ) 実行されていた場合と同じになることを保証。
永続性
永続性は、トランザクションのコミットが成功したら、仮にハードウェアの障害やデータベースのクラッシュがあったとしても、そのトランザクションで書き込まれたすべてのデータは失われないことを約束するもの。
エラーと中断の処理
- 過負荷によってエラーが生じたら、トランザクションをリトライすることによって問題は解決せず、むしろ悪化することになります。こういったフィードバックサイクルを避けるには、リトライの回数を制限し、指数的 (exponential) バックオフを用い、(可能であれば) 過負荷に関係するエラーは他のエラーとは異なる対応をすると良い
- リトライすべきなのは、一時的なエラーの場合 (たとえばデッドロック、分離性違反、一時的なネットワークの障害、フェイルオーバーなど) だけです。高級的なエラー (たとえば制約違反) に対するリトライは的外れ
弱い分離レベル
直列化可能な分離性はパフォーマンス上の負担になるので、多くのデータベースはその負担を避けている。
広く使われている多くのリレーショナルデータベースシステム (これは通常「ACID」だとみなされている) でさえ利用しているのは弱い分離性なので、必ずしも並行性のバグを発生を妨げるとはかぎらない。
Read Committed
read committed が保証することは以下2つ。
- データベースからの読み取りを行った際に見えるデータは、コミットされたもののみであること (ダーティリードは生じない)
- データベースへの書き込みを行う場合、上書きするのはコミットされたデータのみであること (ダーティライトは生じない)
ダーティリードが生じないこと
他のトランザクションはコミットされていないデータを見ることができるなら、それはダーティリードと呼ぶ。
read committed の分離レベルで実行されるトランザクションでは、ダーティリードが発生してはいけない。
ダーティライトが生じないこと
先行する書き込みがまだコミットされていないトランザクションの一部であり、後から行われる書き込みがコミットされていない値を上書きした場合をダーティライトと呼ぶ。read committed 分離レベルで実行されているトランザクションはダーティライトを発生してはいけない。そのため通常、2番目の書き込みは最初の書き込みのトランザクションがコミットもしくは中断されるまで遅らせることになる。
スナップショット分離 は、それぞれのトランザクションがデータベースの一貫性のあるスナップショットから読み取りを行うというもの。すなわち、トランザクションが読み取れるデータは、すべてそのトランザクションの開始時点のデータベースにコミット済みのものだけということ。
データベースはあるオブジェクトについて複数のバージョンを並べて管理する手法を MVCC (multi-version concurrency model、マルチバージョン並行性制御) と呼ぶ。
一貫したスナップショットを見るための可視化ルール
- それぞれのトランザクションの開始時点で、データベースはその時点で進行中の (コミットも中断もされていない) 他のすべてのトランザクションのリストを作成します。それらのトランザクションが行なった書き込みは、後にコミットされるものであってもすべて無視する
- 中断されたトランザクションが行なったすべての書き込みは無視される
- 後から発行されたトランザクション ID を持つトランザクション (すなわち現在のトランザクションが開始された後に開始されたトランザクション) が行なった書き込みは、それがコミットされているか否かにかかわらずすべて無視される
- 他のすべての書き込みはアプリケーションのクエリから見える
スナップショット分離は、データベースにおいて、この分離レベルには別の名前が付けられており、MySQL では リピータブルリード と呼ぶ。
PostgreSQL のリピータブルリードは更新のロストが発生したことを自動的に検出し、問題のトランザクションを中断させる。しかし、 MySQL / InnoDB のリピータブルリードは更新のロストは検出しない。
あるトランザクションでの書き込みが他のトランザクション中の検索クエリの結果を変化させてしまうこの効果をファントムと呼ぶ。