Consensus Commit プロトコル
Consensus Commit は ScalarDB で使用されるトランザクションプロトコルであり、複数の異なるデータベースにまたがるトランザクションを実行するために設計されています。このプロトコルの特徴は、X/Open XA ベースのソリューションとは異なり、下位のデータベースのトランザクション機能に依存せずに ACID トランザクションを実現することです。このドキュメントでは、プロトコルの詳細、その動作方法、保証される分離レベル、インターフェース、採用されているパフォーマンス最適化、および制限事項について説明します。
プロトコル
このセクションでは、Consensus Commit プロトコルの仕組みについて説明します。Consensus Commit プロトコルは、分離を保証するための並行性制御プロトコルと、原子性と永続性を保証するためのアトミックコミットメントプロトコルを使用します。
並行性制御プロトコル
Consensus Commit プロトコルは、並行性制御プロトコルとして楽観的並行性制御 (OCC) を採用しています。OCC は競合が少ないという前提で動作し、ロックを必要とせずにトランザクションを進め、競合が実際に発生した場合にのみ解決します。そのため、OCC は競合の少ない環境で優れたパフォーマンスを発揮します。また、ロック管理が難しい分散環境において特に有益です。
一方、悲観的並行性制御 (PCC) は、競合が多いという前提で動作し、干渉を避けるために使用されるリソースにロックをかけます。そのため、PCC は競合の多い環境で優れたパフォーマンスを発揮します。
ScalarDB の OCC プロトコルは、一般的に使用される OCC プロトコルと同様に、次の3つのフェーズで構成されています:
- 読み取りフェーズ:
- ScalarDB はトランザクションの読み取りセットと書き込みセットを追跡します。ScalarDB はトランザクションがアクセスするすべてのレコードをデータベースからローカルワークスペースにコピーし、書き込みをローカルワークスペースに保存します。
- 検証フェーズ:
- ScalarDB はコミット中のトランザクションが他のトランザクションと競合していないか確認します。ScalarDB は後方検証を使用し、他のトランザクションがトランザクションが読み書きする内容を書き込んでいない場合にのみ、書き込みフェーズに進みます。これらはそれぞれ、読み取り検証と書き込み検証と呼ばれます。
- 書き込みフェーズ:
- ScalarDB はトランザクションの書き込みセットの変更をデータベースに伝播し、それらを他のトランザクションから見えるようにします。
次に説明するように、ScalarDB は検証フェーズでの読み取り検証をスキップする分離モード (分離レベル) を提供し、正確性のために読み取り検証を必要としない一部のワー クロードでより良いパフォーマンスを可能にします。
読み取り検証なしの ScalarDB の OCC は、スナップショット分離と同様に動作します。ただし、単一バージョンで動作し、グローバルスナップショットを作成しないため、読み取りスキュー異常を引き起こします。
アトミックコミットメントプロトコル
Consensus Commit プロトコルは、アトミックコミットメントプロトコル (ACP) として2フェーズコミットプロトコルの変種を採用しています。ScalarDB の ACP は2つのフェーズで構成され、各フェーズには2つのサブフェーズがあり、簡単に説明すると以下のように動作します:
- 準備フェーズ(レコード準備フェーズ + レコード検証フェーズ):
- レコード準備フェーズでは、ScalarDB はトランザクションによって書き込まれたすべてのレコードについて、OCC プロトコルの書き込み検証を実行し、レコードの状態を PREPARED に更新し、すべてのレコードが正常に検証された場合は次のフェーズに進みます。
- レコード検証フェーズでは、ScalarDB はトランザクションによって読み取られたすべてのレコードについて、OCC プロトコルの読み取り検証を実行し、すべてのレコードが正常に検証された場合は次のフェーズに進みます。
- コミットフェーズ(状態コミットフェーズ + レコードコミットフェーズ):
- 状態コミットフェーズでは、ScalarDB は Coordinator テーブルと呼ばれる特別なテーブルに COMMITTED の状態を書き込むことでトランザクションをコミットします。
- レコードコミットフェーズでは、ScalarDB はトランザクションによって書き込まれたすべてのレコードについて、OCC プロトコルの書き込みフェーズを実行し、レコードの状態を COMMITTED に更新します。
レコードを削除する場合、レコードの状態は最初に準備フェーズで DELETED に変更され、その後コミットフェーズで物理的に削除されます。
より詳細な動作方法
プロトコルが各フェーズでどのように機能するかをより詳しく見ていきましょう。
準備フェーズの前
まず、クライアントが ScalarDB (または ScalarDB Cluster ノード) にアクセスして begin
コマンドを発行すると、トランザクションが開始します。トランザクションが開始すると、ScalarDB は下位のデータベースにアクセスするトランザクションコーディネーターとして動作し、最初に UUID version 4 でトランザクション ID (TxID) を生成します。その後、クライアントがレコードの読み取りや書き込みなどの操作を実行した後にトランザクションをコミットする準備ができると、commit
コマンドを呼び出して ScalarDB にトランザクションのコミットを要求し、準備フェーズに入ります。前述のように、ScalarDB はコミット時にトランザクションの読み取りセット (readSet) と書き込みセット (writeSet) をローカルワークスペースに保持します。