PolarDB-X ストレージエンジンのコア技術 | Lizard ブランチ トランザクション管理

> 本記事は[こちらのブログ](https://www.alibabacloud.com/blog/core-technology-of-polardb-x-storage-engine-%7C-lizard-branch-transaction-management_602230?spm=a2c65.11461433.0.0.4ee35355xnGg8q)を参考にしています。
> 翻訳にはアリババクラウドの[ModelStudio](https://www.alibabacloud.com/en/product/modelstudio)([Qwen](https://www.alibabacloud.com/en/solutions/generative-ai/qwen))を使用しております。

# PolarDB-Xの紹介

## 著者: Lantao

### PolarDB-Xとは
[PolarDB-X](https://www.alibabacloud.com/product/polardb-for-xscale) は、クラウドネイティブな分散データベースであり、大規模なデータの保存や高並列処理、複雑なクエリを可能にします。ストレージとコンピュートを分離する共有しないアーキテクチャを採用しており、エンタープライズレベルのクラウドネイティブデータベースとして水平スケーリング、分散トランザクション、ハイブリッドワークロードをサポートし、高い可用性を提供します。また、MySQLおよびそのエコシステムとの高い互換性を備えています。

### アーキテクチャ
![1](https://yqintl.alicdn.com/6cbb83b7e98a1ff6a68ee282558ee5d4167009f3.jpeg)

[PolarDB-X](https://www.alibabacloud.com/product/polardb-for-xscale) は、ストレージとコンピュートを分離する共有しないアーキテクチャに基づいて設計されています。システムは、計算ノード（CN）、データノード（DN）、グローバルメタサービス（GMS）、変更データキャプチャ（CDC）、カラム型ノードという5つのコアコンポーネントで構成されています。

### 水平スケーリング
[PolarDB-X](https://www.alibabacloud.com/product/polardb-for-xscale) はテーブル内のデータを複数のデータノードに水平分割します。データはパーティショニング関数を使用して分割され、PolarDB-Xはハッシュや範囲といった一般的なパーティショニング関数をサポートしています。以下の図はその例です。この例では、accountsテーブルがID列のハッシュ値に基づいてaccount_00からaccount_11まで12個に水平分割され、4つのデータノードに均等に分散されています。

![2](https://yqintl.alicdn.com/63e5356df9bca9a6fb13abe8709ca1c326fadcd9.jpeg)

### 分散トランザクション
データの水平パーティショニングはユーザーにとって透過的です。ユーザーはスタンドアロンデータベースと同じように任意のデータテーブルにアクセスできます。しかし、PolarDB-X自体では、水平パーティションの存在により、アクセスされるデータが異なるデータノード上に存在する可能性があります。例えば、振込シナリオを考えます。ユーザーのアカウントがパーティショニングされたテーブルである場合、同一トランザクションにおける振り込み元と振り込み先が異なるデータノード上にある可能性があります。たとえば、アカウントAliceがDN1上に、アカウントBobがDN2上にある場合、分散トランザクションによって振込の安全性を確保する必要があります。sql
BEGIN;
UPDATE account SET balance = balance - 20 WHERE name = Alice;
UPDATE account SET balance = balance + 20 WHERE name = Bob;
COMMIT;

### XAプロトコル
トランザクションの原子性、一貫性、隔離性、耐久性（ACID）機能を完全にサポートすることは、エンタープライズレベルの分散データベースの中核的な機能です。現在の主流の分散トランザクションモデルには、Percolator、Calvin、XAモデルがあります。成熟したエンタープライズレベルの分散データベースであるPolarDB-Xは、分散トランザクションの実装においてXAモデルに従っています。

#### XAの役割
1998年、OASIS組織は分散トランザクション処理を規制するためにXA仕様を策定しました。XA仕様には次の3種類の役割が定義されています：
- **AP（アプリケーションプログラム）**: データベースサービスの利用者、つまりアプリケーション。
- **TM（トランザクションマネージャー）**: トランザクションの処理を管理し、トランザクションの実行を調整・管理する責任を持つ。
- **RM（リソースマネージャー）**: 特定のリソースを管理・操作する責任を持つ。

XA仕様は、TMとRMが連携できるよう標準的なインターフェースと操作を定義しています。XA仕様の中核は二相コミット（2PC）プロトコルです。2PCプロトコルでは、TMとRMが一連のメッセージを通じて通信し、すべての参加しているRMが分散トランザクション内で対応する操作を行い、最終的にコミットまたはロールバックされることを保証します。このプロセスにおいて、TMはコーディネーターとして機能し、2PCプロトコルの開始と管理を担当します。

![3](https://yqintl.alicdn.com/0dc8d6e767338b156b9a08d290748c3d50c5cc3b.jpeg)

### 二相コミット（2PC）アルゴリズム
前述の図は、分散データベース分野における古典的で成熟した二相コミットアルゴリズムを示しています。具体的な説明に入る前に、ここで2つの関連する概念と用語を追加します：
- **GMS（グローバルメタデータサービス）**: MVCC機能を提供するために、分散データベースはすべての分散トランザクションを順序付けるためにGMSを使用します。
- **トランザクションブランチ**: 分散トランザクションには複数のRMが含まれる可能性があり、各RMには複数のブランチトランザクションが存在します。すべてのブランチトランザクションがまとまって一つの完全な分散トランザクションを構成します。

全体の二相コミットアルゴリズムは次の3つのフェーズに分かれています：

1. **PREPARE**:
   TMはすべての参加するRMに対してPREPAREコマンドを発行します。すべてのRMが要求に応答し成功メッセージを返すと、PREPAREフェーズが完了します。

2. **GMSによる順序付け**:
   TMはGMSにアクセスし、グローバルコミット番号（GCN）を取得して分散トランザクションを順序付けます。このフェーズは必ずしも2PCアルゴリズムに必須ではありませんが、グローバルな順序付けはデータベース業界で読み取りの一貫性を達成するための十分に検証された受け入れられた解決策です。

3. **COMMIT**:
   TMはすべての参加するRMに対してCOMMITコマンドを送信します。すべてのRMが要求に応答し成功メッセージを返すと、分散トランザクションがコミットされます。

### 最適化された分散トランザクションの実装
分散トランザクションを効率的に実装することが、分散データベース全体のパフォーマンスに直接影響します。理論的には、XA仕様は非常に完成度が高く明確ですが、具体的なエンジニアリング実践において議論の余地があります。分散データベースの観点からは、少なくとも2つの最適化方向があると考えられます：
- **並列化を最大化し、ブランチトランザクションをRMs間または単一のRM内で並列実行できるようにする。**
- **異なる役割（TM、RM、GMS）間の相互作用を最小限にする。各相互作用はRTTとなる。**

PolarDB-Xチームは、Lizardトランザクションシステムに基づく「Transaction Group」を提案し、より柔軟で効率的な分散トランザクションの実装をサポートしています。

### Lizardのブランチトランザクション並列化
スタンドアロンデータベースと比較して、トランザクションの分割と並列化は分散データベースのパフォーマンス向上の鍵となる方法の一部です。分散データベースでは、RMs間のブランチトランザクションは疑いなく並列化できますが、単一のRM内のブランチトランザクションが並列化できるかどうかは議論の余地があります。XA仕様では、RMとブランチトランザクションの間のマッピング関係は定義されていません。実際のエンジニアリング実践では、通常、ブランチトランザ
# PolarDB-XにおけるTransaction Groupの導入とその効果

慎重な分析と議論の結果、PolarDB-Xチームは上記の問題の核心的原因は、単一のRM（リソースマネージャ）がブランチトランザクション間の関係を認識していないことであり、各ブランチトランザクションが他と独立しており、効果的に協力できない点にあると考えています。この問題を解決するために、XA仕様に基づき、Lizardトランザクションシステムは「Transaction Group」を提案しました。これは、同じ分散トランザクションに属するブランチトランザクションを一般化し、RM内のブランチトランザクションの共所属関係を確立します。

Transaction Groupは、RMとブランチトランザクションの間に位置し、1つのRM内での複数のブランチトランザクションの連携を促進することを目的としています。これには以下の2つの側面があります：

- 一方で、1つのRM内のマルチブランチトランザクションは依然として独立しており、並行して実行できます。
- 他方で、1つのRM内のマルチブランチトランザクションは同じTransaction Groupに属しており、限定的ではありますがコンテキストを共有できます。![6](https://yqintl.alicdn.com/4550242a23bd94838ba9241745f6030686c18eea.jpeg)

Transaction Groupの概念を導入することで、上記の問題を解決できます。ブランチトランザクションの書き込み隔離の問題に対処する際、Transaction Groupを通じてブランチトランザクション間で可視性を共有できます。同じTransaction Groupに属するブランチトランザクションは、トランザクション開始時のスナップショット制限に関係なく、他のブランチトランザクションの変更を確認できます。

## 最適化されたLizardブランチトランザクションの相互作用

分散データベースでは、MVCC（多バージョン同時実行制御）の要件を満たすために、TM（トランザクションマネージャ）は少なくとも2回GMS（グローバルメタデータサービス）と対話する必要があります。最初はトランザクション開始時で、TMはスナップショット読み取りのためにGMSから現在の最大GCN（グローバルコミットナンバー）を取得します。次に、2フェーズコミット中に、分散トランザクションのシーケンスを行うためにGMSからGCNを取得します。GCNの簡単な比較により、分散データベースは可視性の判断を完了できます。

分散トランザクションは複数のノード間の相互作用を伴うため、スタンドアロントランザクションよりもコストがかかります。これは次の2つの側面に表れます：

- TMとGMSの頻繁な相互作用。
- 2PCプロセスにおけるTMとRMの相互作用。

TMとRMまたはGMSの相互作用を減らすことができれば、分散トランザクションの実行効率は大幅に向上します。実際のビジネスシナリオでは、すべてのアクセスや変更がRM間で行われるわけではなく、かなりの数のアクセスは1つのRMのみを対象としています。PolarDB-Xでは、このような特殊な分散トランザクションを「シングルシャードトランザクション」と呼びます。この場合、分散トランザクションの実行プロセスを簡略化できます。

### 注意点：
- シングルシャードトランザクションには複数のブランチトランザクションが存在することがありますが、これらのブランチトランザクションはすべて1つのRMに配置されています。
- シングルシャードトランザクションは、変更の有無によって「シングルシャード読み取りトランザクション」と「シングルシャード書き込みトランザクション」にさらに分類されます。

### 実行プロセスの簡略化：

- シングルシャードトランザクションが開始する際、GMSから現在の最大グローバルトランザクションコミット番号を取得せずに、RM内で直接単一トランザクションを開始できます。
- シングルシャードトランザクションがコミットされる際、ブランチトランザクションが1つしかない場合は、直接コミット（1フェーズコミット）でき、2PCのオーバーヘッドを回避できます。

## 問題2：読み取りクエリが一貫性のある状態を読み取れない

シングルシャードトランザクションは分散データベースのパフォーマンスを向上させますが、課題ももたらします。同じRM内のシングルシャードトランザクションはGMSと対話しないため、トランザクションエンジン内部で可視性が決定されます。現行のInnoDB実装では、各ブランチトランザクションは独立したトランザクションであり、InnoDBは異なるブランチトランザクション間の関係を認識しません。

例えば、Accountsテーブルの修正を例に挙げます。ある時点で、シングルシャード書き込みトランザクションEとシングルシャード読み取りトランザクションDが開始されます。詳細は以下の通りです：

### シングルシャード書き込みトランザクションE（3つのブランチ）

- ブランチトランザクションa：RM上のaccount_00を修正。
- ブランチトランザクションb：RM上のaccount_04を修正。
- ブランチトランザクションc：RM上のaccount_08を修正。

### シングルシャード読み取りトランザクションD（シングルブランチ）
RM上のアカウントのすべてのパーティションを読み取ります。

シングルシャード読み取りトランザクションDは、ブランチトランザクションaおよびbがコミットされた後、ブランチトランザクションcがコミットされる前に発生します。シングルシャード書き込みトランザクションEとシングルシャード読み取りトランザクションDはどちらもGMSと対話しないため、トランザクションの可視性はInnoDBのローカルトランザクションの可視性チェックに依存します。![7](https://yqintl.alicdn.com/5427c998ede180a134ab2f36f114895a564508b8.jpeg)

分離レベルが「READ COMMITTED」以上の場合、トランザクションDはブランチトランザクションaおよびbの変更を確認できますが、ブランチトランザクションcの変更は確認できません。言い換えれば、AP（アプリケーションプログラム）から見ると、クエリDは書き込みトランザクションEの一部の変更しか確認できず、他の部分は確認できません。これはトランザクションの原子性に違反しています。

### 解決策：Transaction Groupによるコミットステータスの共有

読み取りクエリが一貫性のある状態を読み取れないのは、シングルシャードトランザクションにおけるブランチトランザクション並列化の特殊なシナリオです。本質的には、1つのRM内の複数のブランチトランザクションが完全なトランザクション特性を示さないことが原因です。この問題への対処方法は、書き込みブランチトランザクションの隔離に対処する方法と似ています。Transaction Groupを構築し、コミットステータスを共有できます。

具体的には、1つのTransaction Groupはプライマリブランチトランザクションとセカンダリブランチトランザクションを区別します。プライマリブランチトランザクションは1つだけですが、セカンダリブランチトランザクションは複数存在できます。セカンダリブランチトランザクションのステータスは部分的にプライマリブランチトランザクションによって決定されます。共有コミットステータスに関しては、ブランチトランザクションのコミットステータスはプライマリブランチトランザクションによって決定されます。プライマリブランチトランザクションが