TiDB 楽観的トランザクションモデル
楽観的トランザクションでは、競合する変更はトランザクションのコミット時に検出されます。これにより、同時トランザクションが同じ行を頻繁に変更しない場合、行ロックの取得プロセスを省略できるため、パフォーマンスが向上します。同時トランザクションが同じ行を頻繁に変更する場合(競合)、楽観的トランザクションのパフォーマンスは悲観的な取引よりも低下する可能性があります。
楽観的トランザクションを有効にする前に、アプリケーションがCOMMIT
文でエラーが返される可能性があることを正しく処理できることを確認してください。アプリケーションがこれをどのように処理するか不明な場合は、代わりに悲観的トランザクションを使用することをお勧めします。
注記:
v3.0.8以降、TiDBはデフォルトで悲観的トランザクションモード使用します。ただし、既存のクラスターをv3.0.7以前からv3.0.8以降にアップグレードした場合、これは影響を受けません。つまり、新しく作成されたクラスターのみがデフォルトで悲観的トランザクションモードを使用します。
楽観的取引の原則
分散トランザクションをサポートするために、TiDBは楽観的トランザクションにおいて2相コミット(2PC)を採用しています。手順は以下のとおりです。
クライアントがトランザクションを開始します。
TiDBはPDからタイムスタンプ(時間的に単調増加し、グローバルに一意)を取得し、これを現在のトランザクションの一意のトランザクションID
start_ts
)として取得します。TiDBはマルチバージョン同時実行制御を実装しているため、start_ts
このトランザクションによって取得されたデータベーススナップショットのバージョンとしても機能します。つまり、このトランザクションはデータベースからstart_ts
のデータのみを読み取ることができます。クライアントは読み取り要求を発行します。
- TiDB は PD からルーティング情報 (TiKV ノード間でデータがどのように分散されるか) を受信します。
- TiDB は TiKV からバージョン
start_ts
のデータを受け取ります。
クライアントが書き込み要求を発行します。
TiDBは、書き込まれたデータが制約を満たしているかどうかを確認します(データ型が正しいことを確認するために、NOT NULL制約が満たされます)。有効なデータは、TiDB内のこのトランザクションのプライベートメモリに保存されます。
クライアントはコミット要求を発行します。
TiDB は 2PC を開始し、トランザクションの原子性を保証しながらデータをストアに保持します。
- TiDB は書き込むデータから主キーを選択します。
- TiDB は PD からリージョン分布の情報を受け取り、それに応じてすべてのキーをリージョンごとにグループ化します。
- TiDBは、関係するすべてのTiKVノードに事前書き込み要求を送信します。その後、TiKVは競合バージョンや期限切れバージョンの有無を確認します。有効なデータはロックされます。
- TiDB は事前書き込みフェーズですべての応答を受信し、事前書き込みは成功します。
- TiDB は PD からコミット バージョン番号を受け取り、それを
commit_ts
としてマークします。 - TiDBは、主キーが配置されているTiKVノードへの2番目のコミットを開始します。TiKVはデータをチェックし、書き込み前フェーズで残ったロックを解除します。
- TiDB は、第 2 フェーズが正常に終了したことを報告するメッセージを受信します。
TiDB は、トランザクションが正常にコミットされたことをクライアントに通知するメッセージを返します。
TiDB は、このトランザクションに残っているロックを非同期的に消去します。
メリットとデメリット
上記の TiDB のトランザクションのプロセスから、TiDB トランザクションには次の利点があることがわかります。
- 簡単に理解できる
- 単一行トランザクションに基づくクロスノードトランザクションを実装する
- 分散型ロック管理
ただし、TiDB トランザクションには次のような欠点もあります。
- 2PCによるトランザクションレイテンシー
- 集中化されたタイムスタンプ割り当てサービスが必要
- メモリに大量のデータが書き込まれるとOOM(メモリ不足)が発生する
トランザクションの再試行
注記:
バージョン8.0.0以降、
tidb_disable_txn_auto_retry
システム変数は非推奨となり、TiDBは楽観的トランザクションの自動再試行をサポートしなくなりました。 悲観的なトランザクションモード使用をお勧めします。楽観的ミスティックトランザクションの競合が発生した場合は、エラーをキャプチャし、アプリケーション内でトランザクションを再試行できます。
楽観的トランザクションモデルでは、競合が激しいシナリオでは、書き込み間の競合によりトランザクションのコミットが失敗する可能性があります。TiDBはデフォルトで楽観的同時実行制御を使用しますが、MySQLは悲観的同時実行制御を使用します。これは、MySQLが書き込み型SQL文の実行中にロックを追加し、Repeatable Read分離レベルによって現在の読み取りが許容されるため、コミットで例外が発生する可能性が低いことを意味します。アプリケーションの適応の難易度を下げるため、TiDBは内部的に再試行メカニズムを提供しています。
自動再試行
トランザクションのコミット中に書き込み競合が発生した場合、TiDBは書き込み操作を含むSQL文を自動的に再試行します。自動再試行を有効にするにはtidb_disable_txn_auto_retry
をOFF
に設定し、再試行回数の上限を設定するにはtidb_retry_limit
設定します。
# Whether to disable automatic retry. ("on" by default)
tidb_disable_txn_auto_retry = OFF
# Set the maximum number of the retires. ("10" by default)
# When "tidb_retry_limit = 0", automatic retry is completely disabled.
tidb_retry_limit = 10
自動再試行は、セッション レベルまたはグローバル レベルのいずれかで有効にできます。
セッションレベル:
SET tidb_disable_txn_auto_retry = OFF;SET tidb_retry_limit = 10;グローバルレベル:
SET GLOBAL tidb_disable_txn_auto_retry = OFF;SET GLOBAL tidb_retry_limit = 10;
注記:
tidb_retry_limit
変数は再試行の最大回数を決定します。この変数を0
に設定すると、自動的にコミットされる暗黙的な単一ステートメントトランザクションを含め、すべてのトランザクションが自動的に再試行されなくなります。これは、TiDBの自動再試行メカニズムを完全に無効化する方法です。自動再試行が無効化されると、競合するすべてのトランザクションは、失敗(try again later
メッセージを含む)を最速の方法でアプリケーションレイヤーに報告します。
再試行の制限
デフォルトでは、TiDB はトランザクションを再試行しません。再試行すると、更新が失われ、 REPEATABLE READ
分離破損する可能性があるためです。
理由は再試行の手順からわかります。
- 新しいタイムスタンプを割り当て、それを
start_ts
としてマークします。 - 書き込み操作を含む SQL ステートメントを再試行します。
- 2 フェーズ コミットを実装します。
ステップ2では、TiDBは書き込み操作を含むSQL文のみを再試行します。ただし、再試行中に、TiDBはトランザクションの開始を示す新しいバージョン番号を受け取ります。つまり、TiDBは新しいバージョンstart_ts
のデータを使用してSQL文を再試行します。この場合、トランザクションが他のクエリ結果を使用してデータを更新すると、 REPEATABLE READ
分離性に違反するため、結果に不整合が生じる可能性があります。
アプリケーションが更新の損失を許容でき、 REPEATABLE READ
分離一貫性を必要としない場合は、 tidb_disable_txn_auto_retry = OFF
設定することでこの機能を有効にできます。
競合検出
分散データベースであるTiDBは、主に書き込み前のフェーズにおいて、TiKVレイヤーでメモリ内競合検出を実行します。TiDBインスタンスはステートレスであり、互いを認識しないため、自身の書き込みがクラスタ全体で競合を引き起こすかどうかを認識できません。そのため、競合検出はTiKVレイヤーで実行されます。
構成は次のとおりです。
# Controls the number of slots. ("2048000" by default)
scheduler-concurrency = 2048000
さらに、TiKV はスケジューラ内のラッチの待機に費やされた時間の監視をサポートします。
Scheduler latch wait duration
が高く、遅い書き込みがない場合、現時点では書き込みの競合が多く発生していると安全に結論付けることができます。