TiDBの分散トランザクションモデルについてお話しましょう

TiDBの分散トランザクションモデルについてお話しましょう

[[379689]]

この記事はWeChatの公開アカウント「プログラマーjinjunzhu」から転載したもので、著者はjinjunzhuです。この記事を転載する場合は、プログラマーjinjunzhuの公式アカウントまでご連絡ください。

従来のリレーショナル データベースの分野では、トランザクションの分離レベルを構成することによって、ダーティ リード、ファントム リード、非反復リードの問題を解決することがよくあります。トランザクション分離レベルが異なれば、問題解決の強みも異なります。次の表は、ダーティ リード、ファントム リード、および非反復リードに対するさまざまなトランザクション分離レベルの許容範囲を示しています。見てみましょう:

知らせ:

繰り返し読み取りの読み取りロックは、トランザクションが終了するまで解除されません。

Read committed の読み取りロックは、トランザクションの終了まで待機せず、読み取りが完了するとすぐに解除されます。

もちろん、従来のデータベースでも同時実行制御の問題を解決するために mvcc が使用されていますが、ここでは詳しく説明しません。

上記では読み取りロック、書き込みロック、GAP ロックについて説明しましたが、実際にはさらに多くの種類のロックが存在します。私たち開発者は、楽観的ロックと悲観的ロックについてよく話します。楽観的ロックは実際にはロックされませんが、悲観的ロックでは実際のロックが必要です。分散データベースの分野では同時実行制御も必要であり、楽観的トランザクションと悲観的トランザクションも存在します。

TiDB に関しては、バージョン v3.0 で悲観的トランザクションのサポートが開始されました。 v3.0.8 以降、新しく構築された TiDB クラスターでは、デフォルトで悲観的トランザクションが使用されるようになりました。

従来のデータベースロック

従来のデータベースの楽観的ロックは、主にテーブルにバージョン番号フィールドを追加し、更新時に更新結果に基づいて更新が成功したかどうかを判断します。たとえば、テーブル table_a があり、それにバージョン フィールドを追加します。以下はtable_aのレコードです。

シート

id 名前バージョン
1 ジンジュンジュ 4

id=1 のレコードを更新する場合、SQL は次のようになります。

  1. table_aセットを更新  name = 'xiaoming' 、version = version + 1、 id=1 version=4 の場合

このとき、SQL 実行結果で更新行数が 0 と返された場合は、別のトランザクションがバージョン フィールドを更新したことを意味し、書き込み競合が発生します。ビジネス コードでこの競合を処理する必要があります。高い同時実行性の下で同じレコードに対して多数の変更操作が行われると、必然的に多数の書き込み失敗が発生します。したがって、読み取りが多く書き込みが少ないシナリオには、楽観的ロックの方が適しています。

従来のデータベースの悲観的ロックは物理的なロックです。上記の表では、バージョン フィールドは必要ありません。レコードが 2 つある場合:

id 名前
1 ジンジュンジュ
2 シャオミン

このとき、id=1 のレコードと id=2 のレコードを同時に更新したい場合、1 つのトランザクションで完了するとすると、ロック SQL は次のようになります。

  1. table_aから*を選択し id(1, 2)ある アップデート;
  2. table_aセットを更新 名前= 'zhangsan'  ここで、 id=1;
  3. table_aセットを更新 名前= 'lisi'  ここで、 id=2;

悲観的ロックの問題は、長いトランザクションが発生すると、他のトランザクションがロックを長時間待機する必要があることです。そのため、Oracle では次の最適化が提供されており、変更対象のデータがロックされていることを検出するとすぐに失敗が返されます。

  1. table_aから*選択 アップデート待ち時間なし

パーコレーターモデル

Percolator モデルは、Google が提案し、BigTable 上に構築された分散トランザクション ソリューションです。 Googleの論文は以下の通りです。記事へのリンクは拡張読書[1]にあります:

  1. 「分散トランザクション通知を使用した大規模な増分処理

古典的な電子商取引システムを例に挙げてみましょう。システムに注文、アカウント、在庫の 3 つのテーブルがある場合、ユーザーは購入するたびに注文レコードを追加し、アカウント テーブルで金額を差し引き、在庫テーブルで在庫を差し引く必要があります。これら 3 つのテーブルで操作されるレコードは、それぞれ分散データベースの 3 つのスライスにあります。このとき、分散トランザクションを処理する必要があります。

Percolator アルゴリズム モデルを見てみましょう。

初期

初期段階では、注文テーブルに注文数量が 0 と記録され、アカウントテーブルにアカウント金額が 1000 と記録され、在庫テーブルに製品数量が 100 と記録されていると仮定します。顧客が注文すると、注文テーブルに注文が 1 つ追加され、アカウントテーブルに金額が 100 減額され、在庫テーブルに製品数量が 1 減額されます。各テーブルの初期データは次のとおりです。

上記の表では、「:」の前にはタイムスタンプで表されるデータ バージョンが続き、その後にデータ値が続きます。最初の列はテーブル名、2 番目の列の下位バージョンにはデータが格納され、3 番目の列にはトランザクション操作によってデータに追加されたロックが格納されます。 4 列目の上位バージョンには、保存されたデータ バージョンへのポインターが格納されます。たとえば、バージョン 6 はバージョン 5 のデータへのポインターを格納します: data@5。

事前執筆段階

事前書き込みフェーズでは、調整ノードが各スライスに事前書き込みコマンドを送信します。 Percolator はプライマリ ロックの概念を定義します。事前書き込みフェーズでは、各分散トランザクションには、プライマリ ロックを取得できる変更可能なデータ行が 1 つだけあります。この場合、注文テーブルがプライマリ ロックを取得すると、他のテーブルのロックはこのプライマリ ロックへのポインターとなり、次の表に示すように、セカンダリ ロックと呼ばれます。

事前書き込みフェーズでは、変更されるデータ行ごとにログが書き込まれ、タイムスタンプに従ってトランザクションのプライベート バージョンが記録されます。ここでのプライベート バージョンは 7 なので、他のトランザクションはこれらの 3 つのデータに対して操作を行うことはできません。

マスター ロックを取得する際に、次の状況が発生するとロックが失敗することに注意してください。

1.その他の取引がロックされている。

2. このトランザクションが開始されると、更新対象のデータは他のデータによって更新されます。

コミットステージ

コミット フェーズでは、コーディネータ ノードはプライマリ ロックを持つスライスとのみ通信する必要があるため、この場合は、注文テーブルが配置されているスライスとのみ通信する必要があります。データは次のとおりです。

注文テーブルのロックがなくなり、バージョン 7 を指すバージョン 8 が追加されていることがわかります。これは、注文テーブルが正常に送信され、プライベート バージョンは存在しないが、アカウント テーブルと在庫テーブルのプライベート バージョンはまだ存在することを意味します。これは、Percolator モデルがアカウント テーブルと在庫テーブルを同期的にコミットせず、代わりに非同期スレッドを開始してこれら 2 つのテーブルをコミットし、ロックをクリアするためです。注文テーブルの送信に失敗した場合は、アカウント テーブルと在庫テーブルもロールバックする必要があります。

送信が成功すると、最終データは次のようになります。

コミット フェーズ中、コーディネーション ノードはマスター ロックを持つスライス (ここでは注文テーブルが配置されているスライス) とのみ通信する必要があります。これにより、アトミック性が保証され、コミット中にすべてのノードが成功しないことによって発生するデータの不整合が回避されます。

事前書き込みステージでは、ログとプライベート バージョンが記録されます。アカウント テーブルとインベントリ テーブルが配置されているスライスがコミットに失敗した場合、ログに基づいて再度コミットできるため、データの最終的な一貫性が確保されます。

ここで注意すべき点が 2 つあります。

1. マスターロックの選択はランダムです。たとえば、この例では注文テーブルが選択されていない可能性があります。

2. コーディネーションノードがコミットを送信した後、まず注文テーブルが正常に送信されます。このとき、他のトランザクションがアカウント サービスと在庫サービスから 2 つのデータを読み取りたい場合、2 つのデータはロックされていますが、[email protected] を検索して送信済みであることがわかるため、読み取ることができます。ただし、読み取り時には二次ロックのクリーンアップが必要です。

TiDB 楽観的トランザクション モデル

上記では Percolator モデルを分析しましたが、TiDB の楽観的トランザクションでは Percolator モデルが使用されます。

TiDB は MVCC をサポートします。トランザクションが開始されると、タイムスタンプ start_ts が現在のトランザクション ID および MVCC のスナップショット バージョンとして使用されます。後続の読み取り要求では、現在のスナップショット バージョンでデータが読み取られます。データ検証が成功すると、クライアントは 2 段階のコミットを実行します。次のタイミング図を見てみましょう。

最初の段階では、クライアント要求を受信した後、TiDB はまず変更するキャッシュされたキーから最初の事前書き込み要求を見つけ、このキーにプライマリ ロックを追加した後、成功を返します。次に、TiDB はこのトランザクションの他のすべてのキーに事前書き込み要求を送信し、これらのキーにセカンダリ ロックを追加した後、成功メッセージを返します。

第 2 段階では、事前書き込みが成功した後、TiDB はまず PD から現在のトランザクションの commit_ts としてタイムスタンプを取得し、次にプライマリ ロック キーにコミット要求を送信します。プライマリ ロック キーがデータを正常にコミットすると、プライマリ ロックがクリアされ、成功が返されます。 TiDB はプライマリ ロック キーの成功メッセージを受信すると、クライアントに成功メッセージを返します。

楽観的トランザクションの競合検出は、主に事前書き込みフェーズで行われます。現在のキーがロックされていることが検出された場合は、待機時間が発生します。この時間後にロックが取得されない場合は、失敗が返されます。したがって、複数のトランザクションが同じキーを変更すると、必然的に多数のロック競合が発生します。

注: TiDB には再試行メカニズムもありますが、デフォルトでは無効になっています。 TiDB の再試行では start_ts が再取得されますが、データは再読み取りされないため、繰り返し読み取り分離レベルは保証されません。詳細については、TiDB の公式ドキュメントを参照してください。

TiDB 悲観的トランザクション モデル

TiDB はバージョン v3.0 から悲観的トランザクションを導入しました。

注意: v3.0.7 以前のバージョンで作成されたクラスターをより高いバージョンにアップグレードした後も、楽観的トランザクションはデフォルトで引き続き使用されます。新しく作成されたクラスターのみが、デフォルトで悲観的トランザクションを使用します。次のコマンドを使用して悲観的なトランザクションを開始することもできます。以下の最初のステートメントは、TiDB システム パラメータを変更します。次の 2 つのステートメントはシステム パラメータを無視し、優先順位が高くなります。

グローバル tidb_txn_mode を '悲観的' に設定します。

悲観的に始める;

楽観的に始める;

MySQL との互換性を保つために、TiDB の悲観的トランザクションは MySQL と非常に似ています。悲観的トランザクションは、繰り返し読み取りとコミット読み取りの 2 つの分離レベルをサポートします。デフォルトでは繰り返し読み取りが使用されます。 TiDB では、楽観的トランザクションと悲観的トランザクションが共存できます。楽観的トランザクションが最初に使用され、ロックの競合がある場合にのみ悲観的トランザクションが使用されます。

悲観的なトランザクションを使用するステートメントは次のとおりです。

  1. 更新削除挿入選択 のために アップデート 

TiDB の悲観的なトランザクションについては、いくつか注意すべき点があります。

  • SELECT FOR UPDATE ステートメントは、変更された行ではなく、最後にコミットされたデータに悲観的ロックを適用します。
  • TiDB は GAP ロックをサポートしていないため、FOR UPDATE ステートメントの WHERE 条件で範囲条件が使用されている場合でも、挿入は可能です。たとえば、ID が競合しない場合は、次の SQL ステートメントを正常に挿入できます。
  1. SELECT * FROM t1 WHERE id BETWEEN 1 AND 10 FOR  アップデート;
  • innodb_lock_wait_timeout変数で待機ロックのタイムアウトを設定できます。デフォルトは50秒です。
  • FOR UPDATE NOWAIT構文はサポートされていません
  • Point Get 演算子と Batch Point Get 演算子がデータを読み取らない場合でも、指定された主キーまたは一意のキーはロックされ、他のトランザクションが同じ主キーをロックしたり書き込んだりすることがブロックされます。
  • 悲観的なトランザクションの実行中に DDL 操作が実行されると、操作は成功しますが、その後トランザクションはコミットに失敗します。
  • 悲観的トランザクションの実行時間には上限があり、デフォルトでは10分に設定されていますが、パラメータで設定できます。

要約する

ビジネス シナリオの複雑さにより、必然的に楽観的なトランザクションの競合が増えますが、これは TiDB の後続バージョンが悲観的なトランザクションに変わった重要な理由でもあります。 TiDB では、楽観的トランザクションと悲観的トランザクションが共存できます。

さらに読む:

[1]https://www.cs.princeton.edu/courses/archive/fall10/cos597B/papers/percolator-osdi10.pdf

[2]https://docs.pingcap.com/zh/tidb/stable/pessimistic-transaction

[3]https://docs.pingcap.com/zh/tidb/stable/optimistic-transaction

[4]https://pingcap.com/blog-cn/percolator-and-txn/

<<:  なぜ Kafka を諦めて Pulsar を選んだのでしょうか?

>>:  SAPと提携し、Houhou Technologyがインテリジェント製造の夢を実現

推薦する

.NET マイクロサービス アーキテクチャの実践: 概念から展開までの包括的なガイド

ソフトウェア システムが成長し、複雑になるにつれて、マイクロサービス アーキテクチャはその柔軟性、ス...

簡単な説明: SEO 記事に内部リンクが必要な理由

最適化のためには、内部ページには内部リンクは必要なく、特に記事コンテンツにはアンカー テキスト リン...

ユーザー行動研究に基づく不動産ネットワークマーケティングモデル

2010年に始まった不動産規制は、不動産業界全体を最前線に押し上げました。銀行融資は厳しくなり、購入...

WEBデザインのレイアウトでユーザーが関連情報を効率的かつ正確に取得できるようにする方法

まずは、マイクロソフト社が開発し、社内では「タイポグラフィベースのデザイン言語」と呼ばれている「Me...

ウェブサイトのSEOではキーワードの選択スキルを無視できない

従来の SEO 時代では、キーワードの選択は検索エンジンでのランキングを意味し、ある程度ウェブサイト...

evlgaming-KVM VPS 5.4% オフ/1g メモリ/月額 6.5 ドル

2010 年に設立された evlgaming はカンザス州に登録された会社であり、サブブランドである...

2013年の嵐の中心にいるSEOへ

SEO には秘密はありません。信じられないかもしれませんが、それが真実です。私はいつも、あなたが素晴...

ResearchAndMarkets: 世界のクラウド コンピューティング サービス業界は 2027 年に 3,131 億ドルに達する

海外メディアの報道によると、市場調査会社ResearchAndMarketsが発表した報告書では、C...

マルチアクセス エッジ コンピューティングがスマート シティの未来に与える影響

世界中の都市は、住民に経済的、社会的、環境的利益をもたらすために、スマート シティ テクノロジーへの...

Virmach のニューヨーク データ センターの AMD シリーズ VPS の簡単なレビュー

ニューヨークは、virmach の新しい AMD シリーズ VPS の 9 番目のデータ センターで...

パブリッククラウドにさらなる成長の余地を与えてください

もちろん、パブリック クラウドはまだ初期段階にあり、何らかの問題が避けられないことを認識しておく必要...

30日間の降格後、百度の権威を回復する方法

みなさんこんにちは。今日もここでお会いできて嬉しいです。最近は体重の回復に忙しくて、とても疲れていま...

2019 年のクラウド コンピューティングの 10 大トレンド: クラウドが人工知能を実現する主な手段に

10年間の発展を経て、中国のクラウドコンピューティングは社会経済で広く利用されるようになりました。市...