ほとんどのデータベースシステムでは同時実行制御にロックが使用されますが、PostgreSQLは少し異なる方法で動作します。つまり、マルチバージョンモデル(Multi-Version Concurrency Control、略してMVCCとも呼ばれます)を使用してデータの一貫性を維持します。その結果、データベースにクエリを実行すると、基になるデータの現在の状態に関係なく、各トランザクションはしばらく前のデータのスナップショットを参照します。これにより、同じデータに対する他の同時トランザクション更新によって引き起こされる可能性のある不整合なデータをトランザクションが表示することがなくなり、データベースセッションごとにトランザクションが分離されます。このブログ記事では、MVCCプロトコルの仕組みの概要を簡単に説明するとともに、MVCCアプローチの長所と短所についても説明します。
MVCCプロトコルの説明
ロックモデルとMVCCの主な違いは、後者では、読み取りによって書き込みがブロックされたり、書き込みによって読み取りがブロックされたりすることがないことです。
MVCCでは、全てのトランザクションには、(a)トランザクションが開始された時、および(b)トランザクションがフィールド、レコード、テーブルなどの特定のデータ項目を更新した時を示すトランザクションタイムスタンプがあります。このデータ項目の新しいバージョンは、古いバージョンも保持しながら作成されます。各バージョンには次のものが提供されます:
- それを作成したトランザクションのタイムスタンプ(つまり、トランザクションの開始時刻)を示す書き込みタイムスタンプ、および
- 読み取りを行った全てのトランザクションの最新のタイムスタンプを示す読み取りタイムスタンプ。
MVCCプロトコルの基本的な考え方は、トランザクションマネージャーは、タイムスタンプの時点で完全に実行されている全てのトランザクションと一致する方法で操作が許可される場合にのみ操作を許可するというものです。これを推定実行順序といいます。データベース研究者の Jan Hidders は、トランザクションマネージャーがこれをどのように実現するかを次のように説明しています:
トランザクションが項目を読み取りたい場合、推定実行順序で読み取ったであろうバージョンへのアクセスが与えられます。これは、自身のタイムスタンプの直前に最新の書き込みタイムスタンプを持つものになります。例えば、書き込みタイムスタンプが5、12、および20のバージョンがあり、トランザクションのタイムスタンプが14である場合、書き込みタイムスタンプが12のバージョンが、推定実行順序でこのトランザクションによって読み取られるバージョンになります。
トランザクションが項目を書き込みたい場合は、以前に許可された読み取り操作がないかどうか、および推定実行順序では要求された書き込み操作によって引き起こされた新しいバージョンを読み取る操作がないかどうかがチェックされますが、許可されていた場合は別のバージョンの読み取りが行われます。例えば、書き込みタイムスタンプが5、10、および16のバージョンがあると仮定します。さらに、これらのバージョンの読み取りタイムスタンプがそれぞれ8、12、および20であると仮定します。タイムスタンプ 11のトランザクションが項目を更新したい場合、書き込みタイムスタンプ 10のバージョンがタイムスタンプ 12のトランザクションによって読み取られたため、問題が発生します。したがって、タイムスタンプ 11のバージョンが作成された場合、タイムスタンプ 12のトランザクションは、推定実行順序では、タイムスタンプ 10のトランザクションによって作成されたバージョンではなく、現在作成されようとしているタイムスタンプ 11のバージョンを参照することになります。一方、タイムスタンプ 14のトランザクションが項目を書き込みたい場合は、これは問題ありません。なぜなら、推定実行順序のt=12以降、アイテムはt=16で更新される瞬間までどのトランザクションによっても読み取られなかったからです。
MVCCの長所と短所
長所:
- 上記の説明からわかるように、全ての読み取り操作は常に即時に許可されます。通常、ロックベースのアプローチでは、既存の書き込みロックが原因で読み取りロックが拒否される可能性があるため、これは当てはまりません。
- 通常のロックベースのアプローチよりも多くの書き込み操作を即座に実行できる傾向があります。
短所:
- 書き込み操作が拒否された場合、トランザクションをロールバックするか再起動する以外に選択肢はありません。一度更新が拒否されると、後で再試行しても拒否されます。これは、通常はロックが使用可能になるまで待つことができるロックベースのアプローチとは異なります。MVCCが楽観的プロトコルとして分類されるのはこのためです。競合がなければ非常に効率的ですが、競合が発生すると、多くの作業を元に戻さなければならない場合があります。
- 項目のバージョンが多いと、かなり多くのストレージスペースが必要になる場合があります。ロックベースのアプローチでは、1つのバージョンのみを保存する必要があります。
- 不要になったバージョンを削除すると、オーバーヘッドが発生する可能性があります。
PostgreSQLのマルチバージョン同時実行制御に関する最終的な考え方
このブログ記事では、MVCCプロトコルの仕組みの概要を説明し、その長所と短所をいくつか紹介しました。
PostgreSQLの使用に興味がありますか? Navicat 16 for PostgreSQL を14日間無料でお試しいただけます。