DBの基礎理論(2)【排他制御、ロック、トランザクション管理、ACID特性】

 ここでまとめている内容はDBの基礎理論(1)に引き続き「データベーススペシャリスト試験」等で出題される、DBの実装理論です。
 具体的に実装する方法は各DBMS製品ごと微妙に違いますが、理論として全ての製品に共通するものです。特にトランザクションはDBにおいて根幹をなす非常に重要な理論ですので、抑えておくようにして下さい。

1.排他制御(ロック)

排他制御(ロック)

 排他制御(ロック)とは、複数のユーザーが同じレコードを同時に利用した場合でもデータの矛盾を発生させないための仕組みのことを言います。
 もし、ロックを行わなかった場合、複数のユーザーが同時にデータ更新を行った場合、不整合が発生してしまいます。この為、一方のユーザーが更新中のデータは他のユーザーが更新できないようにデータをロックすることで、データの整合性を守ります。


ロックの種類

 大きくは
 共有ロック
 占有ロック
 の2つです。
 占有ロックは他からのアクセスを一切認めないロック。共有ロックはデータの参照のみ認めるロックです。不整合を発生させないためだけで言えば、占有ロックだけあれば良いのですが、占有ロックの場合、参照のみのユーザーまでロック解除されるまでデータ参照を待たされてしまう為、データベースのレスポンスが大幅に悪化してしまいます。これを防ぐ目的で、共有ロックと占有ロックを併用します。

2.トランザクション

 データの整合性を保証するための、根幹となる仕組みです。

2-1.トランザクションとは

 トランザクションを説明する際のお決まりの例ですが、
 銀行の振込みを行う場合
 ・振込元の口座から減額
 ・振込先の口座の増額
 この2つの処理を一連の連続処理として実行しないとデータの整合性が取れない状況になります。これを避けるため減額・増額をひとまとめの処理とする仕組みがトランザクションです。いわゆる処理のグループ化です。2つの処理を1つのトランザクションとします。実際の利用例はこちらをご覧下さい。

 そしてもう一つ重要なことは連続処理でなく、例えば元のデータに1加算するというような単独処理であってもトランザクションということです。この場合1つの処理ですのでもちろんそれ以上の分割できない単独のものですが、処理が開始されデータベースに正常にコミットされるまでは1つのトランザクションとして扱われます。

 つまりトランザクションとはデータベースへの処理単位と言えます。

2-2.トランザクションのACID特性

 下記トランザクションの特性を示す用語です。4つの特性の頭文字を取っています。

 原子性、一貫性に関しては必ず保証されなければならない原則ですが、耐久性、独立性については厳格化しすぎるとレスポンスが悪化するため妥協点を探る運用となります。


 原子性: Atomicity

 トランザクションを構成するすべての処理は、完全に実行するか実行されないかのどちらかになること。コミット(Commit) Or ロールバック(RollBack) です。
 障害が発生した際に一連のデータ更新処理をしていたとして、「途中までは更新しましたが、残りは障害が発生したので更新しませんでした。」ではなく、この場合最後まで更新を遂行できないのであれば、途中までの更新もしないで更新前の状態にロールバックするという原則です。


 一貫性: Consistency

 トランザクションの開始、終了各々の時点でデータの整合性が保たれていることです。
 一番分かりやすい例はプライマリーキーに重複したデータをインサートしようとするとデータベース側がエラーにして自動でロールバックしますが、これが一貫性を保証する仕組みの一例です。


 耐久性: Durability

 トランザクションが正常に終了した(コミットした)結果は、障害が発生してもデータベースから失われないことです。

 DBMSにおいてデータベースのデータファイルとトランザクションファイルはファイルとして分離しており、データファイルにはコミットされた結果、トランザクションファイルには処理中のトランザクションが各々格納されます。
 そしてコミットのタイミングごとトランザクションファイルからデータファイルへ結果が反映される仕組みです。これより耐久性が保証されます。
 もし、この仕組みではなくデータファイルのみだとするとどうなるでしょう。連続処理の途中で障害が発生したとしても途中の結果までデータファイルに結果が反映されてしまうので、簡単にコミット前のデータを戻すことができません。一方、各々分離すれば、障害が起こった際もコミットした結果はデータファイルに保存されており、容易にロールバックし元のデータに戻すことができます。

 トランザクションとは何?と聞かれた際に
 ・複数の処理をひとまとめにするもの
 ・障害を復旧するために必要なファイル
 という2つの答えが立場により返ります。「複数の処理をひとまとめにするもの」はプログラマーの考えるトランザクション。「障害を復旧するためのファイル」はサーバー運用の考えるトランザクションです。ここで言う耐久性はサーバー運用側の立場。データファイルとトランザクションファイルを分けて障害に備えるということです。

   話がややこしくなるので、以下ご参考程度に。DBMSではトランザクションファイルはログファイルとも呼ばれDBの処理の都度記録されるのですが、このログファイルとデータファイルの書き込みの間にWALと呼ばれる中間書き込みが有り、本来コミットのタイミングでデータファイルに書き込まれるはずが、データベースのレスポンス向上を目的にWALにしか反映しないというDBMSがあります。
 WALは平テキストでデータファイルはTree構造のデータです。Tree構造のデータファイルへの書き込みには時間がかかるので、DBMS側でタイミングを計ってWALをデータファイルに反映させます。
 さらにいちいちI/Oを発生させると遅延につながるため、メモリ等の揮発性領域にのみ保存し(インメモリ)、一定の間隔でその結果をHDD等の不揮発性領域に保存するという手法を取るDBMSもあります。
 あくまでこの耐久性については一般論と考える方が良いです。耐久性を追求すると今度は速度遅延につながります。

 独立性: Isolation

 DBMSの設定によります。デフォルトの設定では保証されません。

 トランザクションはそれぞれ独立しており、各々のトランザクション処理が他トランザクションの処理に影響を与えないことです。

 だだし、この独立性に関しては以下の分離レベルの設定によります。独立性が保証されるのはserializableのときだけですが、このserializableを設定してしまうとDBのアンロック待ちの遅延が大量発生しレスポンスが大幅に悪化します。そして最悪デットロックも起こり得ます。
 主なDBMSではデフォルトでread committedもしくはrepeatable readが設定されています。これらが実際の落とし所だと思います。

分離レベル 起こりうる障害 レスポンス
ダーティーリード ファジーリード ファントムリード ロストアップデート
read uncommitted
read committed
repeatable read
serializable


ダーティーリード:
 自身のトランザクションから別のトランザクションの未コミットなINSERT、UPDATE、DELETE結果が見えてしまうこと。


ファジーリード:
 自身のトランザクションから別のトランザクションのコミットされたINSERT、UPDATE、DELETE結果が見えてしまうこと。


ファントムリード:
 自身のトランザクションから別のトランザクションのコミットされたINSERT結果が見えてしまうこと。


ロストアップデート:
 自身のトランザクションが別のトランザクションのコミットされたUPDATE結果を上書きしてしまうこと。



おすすめの関連記事

DBの基礎理論(1)【データモデル、関数従属性、キー属性、正規形】
データモデル、正規形等データベースの基礎理論についてまとめました。
現場での実践SQL(1)
現場での実践的なSQL知識をまとめています。(1)では抽出がSQLの根本となることをまとめています。