トランザクション概観

データベース

概要

トランザクションについてまとめる。

トランザクション

データを正しく保つための手法。DB固有の概念ではなく、一つの理論として独立している。

多数のクライアントからDBサーバーに対して同時アクセスが発生するような状況や、DBサーバーまたはアプリケーションが更新処理途中にクラッシュするという状況などからデータの整合性を守りたい時に必要とされる。

トランザクションが提供する機能は2点ある。

  • 同時実行制御
    • 同時アクセスによって発生し得るデータ不整合を防ぐこと。
  • クラッシュリカバリ
    • DBサーバーやアプリケーションがクラッシュしても自動的にリカバリ処理が行われること。

データ不整合を起こさないためには、処理の並列化を行わず、1つずつ順番に直列化されたスケジュールで実行することで実現可能であるが、多数のトランザクションが同時実行されている状況において直列化されたスケジュールで実行することは現実的ではない。

データが正しく保存されている状態とは、この直列化されたスケジュールでトランザクションが実行されたときと同じ結果になっている状態であると定義することができる。

現実的な実行制御のためには、直列化されたスケジュールと同じ結果になるスケジュールを選択することができるかどうかというスケジューラの性能が影響する。

スケジューラの性能は、

  • どれくらい多くのトランザクションを並列化することができるか
  • 最適なスケジュールを探し出す計算コスト

が主な指標となる。

RDBでは、ロッキングスケジューラというロックを用いたスケジューラが一般的に広く用いられている。

ACID特性

トランザクション処理において求められれる特性。

RDBでなくとも、これらの特性を満たしているものはトランザクションを実装していると言える。

Atomicity(原子性) - コミットメント制御

トランザクションに含まれる操作がすべて成功(Success)か失敗(Abort※)になる性質。

※SQLではROLLBACKだがトランザクションではABORTという。

トランザクションは成功することが保証されているのではなく、失敗したら全て取り消されることが保証されているに過ぎない。

原子性は、トランザクションがエラーになった場合にロールバックできること、と言い換えることができる。

Abortされたときのエラー処理(リトライ)を実装していないアプリケーションはトランザクションの扱いが間違っているということになる。

Consistency(一貫性) - 排他制御

トランザクション実行前後においてデータの一貫性が損なわれてはならないという性質。

トランザクション実行後、DBはデータの変更があってもデータの不整合がない状態を保つ必要がある。

トランザクション実行後、DBはある一貫性の状態から別の一貫性のある状態へ遷移すること、と言い換えることができる。

データの一貫性を保証するのはDBではなく、アプリケーションとなる。RDBであれば、RDBのデータモデルであるリレーショナルモデルが一貫性があるかどうかの判断ロジックとなる。

Isolation(分離性) - 排他制御

同時に実行している復数のトランザクションが互いに影響を与えないという性質。

個々のトランザクションの実行結果は、直列で実行されたトランザクションと同じ結果でなければならない、と言い換えることができる。

分離性はトランザクションの同時実行制御をよく表現している。

Durability(永続性) - 障害回復機能

一旦コミットが完了したトランザクションが消失されないという性質。

確定したトランザクションが取り消しされないという実装があり、クラッシュしてもリカバリによってクラッシュ前のデータの状態まで復元可能であるという性質である。

クラッシュリカバリ後はコミットしたデータだけが残るため、クラッシュリカバリが完了すればコミット完了時点においてDBの一貫性が保証された状態になる。

トランザクションが防ぐべき異常

トランザクションにとって、同時実行制御の観点からあってはならない状態について列挙する。

以下トランザクションはTXと省略する。

ロストアップデート

TX1が書いたデータと、同じデータを別のTX2が更新するとき、TX2はTX1が書いた結果を見て、次のデータを決める必要があるが、TX2がTX1が更新する前のデータを元に同じデータを更新すると、TX1によって行われた更新は消失してしまう。

インコンシステントリード

TXの実行結果が別のTXの実行結果に影響を与えると、TXを読み取ったデータの一貫性が失われてしまう。

ダーティリード

別のTXでコミットされていないデータが読み取れしまう現象。

TX1の更新後、コミットしていないデータを別のTX2が読み取った場合、TX1がAbortした際にTX2が読み取ったデータは正しいものではなくなってしまう。

ノンリピータブルリード(ファジーリード、非再現リード)

別のTXで更新されたデータを読み取ることで、データの一貫性が失われる現象。

1つのTX内で同じデータを複数回読み取っている途中で、TXが書き込みをしていないにも関わらず、データが変わってしまう現象。

ファントムリード

別のTXで挿入されたデータが見えることで、データの一貫性が失われる現象。

TX1で一定範囲を読み取っていると途中で、TX2でデータを追加または削除してコミットした際、TX1で幻のようにデータが反映されてしまう。

スケジュールとロック

トランザクションが防ぐべき異常は、本来実行してはいけないものであり、そのようなスケジュール発生を防ぐ必要がある。

RDBでは、ロック使った排他制御が一般的に用いられている。

ロックは、操作対象の行を、操作が行われる前にロックをかけることによってクエリ実行の際のデータの一貫性を守ることができる。

ロックがかかることにより、ロックがかかった行へのアクセスを必要とするトランザクションはブロックされる。

デッドロック

RDBでは、行レベルロックあるいはページロックといった実装においてデッドロックという問題が発生し得る。

デッドロックとは、2つのトランザクションが互いをブロックし合う状態になり、処理が進まなくなる状態のことをいう。

回避策は実装により異なる。

ロックの種類

共有・占有はDBの機能で、楽観的・悲観的ロックは方針。

共有ロック(READロック)

データのREADをする時に使うロック。他のトランザクションはWRITEができなくなる。

専有ロック(WRITEロック)

データをWRITEする時に使うロック。他のトランザクションはREADもWRITEもできなくなる。

楽観ロック(楽観的排他制御)

データへの同時アクセスは発生しないだろうという楽観的な前提に基づく方式。

更新対象のデータがデータ取得時と同じ状態であることを確認してから更新することでデータ不整合を防ぐ。

データが同じ状態であるか判断するためのカラムをロックキーという。

悲観ロック(悲観的排他制御)

データへの同時アクセスが頻繁に発生するだろうという悲観的な前提に基づく方式。

更新対象のデータを取得する際に、別のトランザクションから更新されないようにロックをかけることでデータ不整合を防ぐ。

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

分離レベル 分離性 ダーティリード インコンシステントリード ロストアップデート ファントムリード
READ-UNCOMMITTED 低い
READ-COMMMITED ×
REPEATABLE-READ × ×
SERIALIZABLE × × × ×

参考