長安チェーンの並列スケジューリングの仕組み (1): トランザクションのスケジューリングと競合検出プロセス

長安チェーンは効率的な並列スケジューリングを使用してトランザクションを実行します。長安チェーンのトランザクション スケジューリング、競合検出、および DAG 構築プロセスを理解することは、開発者が長安チェーンの並列スケジューリングの動作メカニズムをより深く理解し、高品質で競合の少ないスマート コントラクトを作成するのに役立ちます。ブロックチェーンアプリケーションをより適切に構築できます。

2 つの記事を使用して、長安チェーン トランザクションのスケジューリング、競合検出プロセス、DAG 構築プロセスについて説明します。

I. 概要

1. 基本的な考え方

トランザクションのスケジューリング実行は、ブロックチェーン トランザクション実行の中核プロセスであり、コンセンサス ラウンドのパフォーマンスに大きな影響を与えます。トランザクション実行の本質は、特定のキーの読み取りと書き込みです。ブロックチェーンでは、多くの場合、ブロックには複数のトランザクションが含まれます。トランザクション実行後の最新のデータ状態は、「ワールド状態」と呼ばれます。ブロック内のトランザクションをスケジュールして実行するには、最も単純なシリアル実行などの決定的スケジューリングと、非決定的スケジューリングの 2 つの方法があり、スケジューリングの実行パフォーマンスを向上させるために、多くの場合、並列性を高めます。トランザクションの実行。トランザクションの並列実行では、すべてのトランザクション実行の「正確性」と「順序性」を確保する必要があります。

● 正確性: トランザクションの実行中、トランザクションが依存する事前十分条件を破棄することはできません (本質的に、読み取り/書き込み競合トランザクションを同時に実行することはできません)。

● 順序性: すべてのノードは、ブロック内のトランザクションを同じ実行順序で実行して、一貫した世界状態を取得できます (マスター ノードは、トランザクションを実行する順序をスレーブ ノードに伝える必要があります)。

2. シーンの構築

理解を容易にするため、単純な銀行融資契約を構築しましたが、子会社が銀行に融資を申請する場合、上位会社または本社の預金によって保証される必要があります。融資額以上の場合、銀行は子会社に融資を提供します。融資は実行され、直ちに子会社の口座に入金されます。

写真

図 1.1 世界の状態

k1 を本社口座、k2 を第 1 レベルの子会社口座、k3 を第 2 レベルの子会社口座とすると、現在の世界の状態は上図に示されています。1 つのブロックには、銀行からの融資を申請するための次の 4 つのトランザクションがあります。

写真

図 1. ブロック内の 4 つの競合するトランザクション

● tx0 は、第 1 階層子会社 k2 が本社 k1 の預金を保証として銀行に融資を申請することを意味します。

● tx1 は、第 2 レベル子会社 k3 が第 1 レベル子会社 k2 の預金を保証として銀行に融資を申請することを意味します。

● tx2 は、第 2 階層子会社 k3 が本社 k1 の預金を保証として銀行に融資を申請することを意味します。

● tx3 は、第 1 階層子会社 k2 が本社 k1 の預金を保証して銀行に融資を申請することを意味します。

その中には、tx0 と tx1 の間に読み取り/書き込み競合が存在し、tx1 と t2 の間に書き込み/書き込み競合が存在し、tx3 と tx1 の間に書き込み/読み取り競合が存在します。

2. トランザクション競合の検出

ブロック内のすべてのトランザクションが順番に実行される場合、すべてのトランザクション実行の正確さには影響しません。2 つのトランザクションが同じキーを読み書きした場合でも、後続のトランザクションは前のトランザクションより前に実行されます。後の「ワールド ステート」で実行されます。 、トランザクションの実行結果も正しいです。

写真

図 2.1 トランザクションのシリアル実行

上記の tx0 と tx1 を例にとると、本社 k1 口座残高が 100 元 (v1)、第 1 レベル子会社 k2 と第 2 レベル子会社 k3 の口座残高がそれぞれ 0 元の場合、その預金額は上位企業を保証人として100元の融資を申請する。読み取り/書き込みの競合はありますが、シリアル実行では、後のトランザクションは前のトランザクションの最新の世界状態に基づいて実行され、2 つのトランザクションの実行結果は決定的です。具体的には、上の図には 2 つの状況があります。

● 最初のケースでは、tx0 が最初に実行され、融資が成功し、第 1 レベルの子会社 k2 の残高が 100 元になり、後に実行された tx1 は第 1 レベルの口座残高が銀行融資条件を満たします。子会社k2は100元となり融資は成功します。

● 2 番目のケースでは、第 1 レベルの子会社 k2 の残高が 0 であるため、tx1 が最初に実行され、融資は失敗しますが、本社 k1 の残高はまだ 100 元であるため、後で実行された tx0 は成功します。

逐次実行の場合は上記2つのどちらか一方のみが実行されますが、どちらを実行してもトランザクションの実行結果は確実であり、ビジネスロジックに則った正しい結果でもあります。

ただし、並列スケジューリングを使用する場合、トランザクションの実行スケジュールのパフォーマンスを向上させるために、状況はさらに複雑になります。並列実行されるトランザクションに競合があることを検出 (正確性の保証) する必要があり、競合するトランザクションを検出する必要があります。また、これらのトランザクションをこの順序で実行するように他のノードに指示する必要があります(順序を保証するため)。

次に、このパートでは正確性を確保する方法を紹介し、後半では順序を確保する方法を紹介します。

1. 読み取り/書き込み、書き込み/読み取りの競合を再実行する必要がある

トランザクション実行の正しさを判断する場合、読み取り/書き込み競合および書き込み/読み取り競合は本質的に一種の競合です。つまり、トランザクションが依存する読み取りセットの内容が、実行プロセス中に他のトランザクションによって変更され、トランザクション正しい 実行の前提条件が崩れた場合、トランザクションの実行結果の正当性は保証されないため、トランザクションを再実行する必要があります。

写真

図 2.2 トランザクションの読み取りと書き込みの競合

上図のように、tx0とtx1を並行して実行する場合、左図の状況に従って実行すれば、読み書きの競合は発生せず、正当性の問題も発生しないため、後者のトランザクションは再実行する必要はありません。しかし、右図の状況で実行すると、tx1とtx0の間に読み書き競合が発生し、tx1がトランザクション実行開始時に依存していたk2が、tx1の実行中にtx0によって変更されてしまいます。この場合、実行後に tx1 を更新する必要があります tx0 を実行した後、最新の「ワールド状態」を元に再実行することでのみ正しい結果が得られます。

写真

図 2.3 トランザクションの読み取り/書き込み競合 (書き込み/読み取り競合から発展した読み取り/書き込み競合)

上図のように、tx1とtx3を並列実行する場合、図2.3の左側の状況に従って実行すれば、読み書きの競合は発生せず、正当性の問題も発生しませんので、後者のトランザクションを再実行する必要はありません。ただし、右側のケースでは、tx1 と tx3 の間に読み取り/書き込みの競合があり、後から実行される tx1 が依存する k2 が tx1 の実行中に tx3 によって変更されるため、後から実行される tx1 を再実行する必要があります。 -正しい結果を得るために実行されます。ここで、直観的な書き込みと読み取りの競合が並列実行中に読み取りと書き込みの競合に発展すると、実行後のトランザクションの正確さに影響を与えることが強調されます。

2. 競合せずに読み取りと読み取り、書き込みと書き込みを直接上書き

読み取りと読み取りの状況については、書き込みがない場合、同じキーを同時に読み取った場合、取得される値はすべて一貫性のある正しい結果であり、並列の正当性の問題は発生しないため、あまり説明しません。実行。

書き込みと書き込みについても比較的単純で、書き込みと書き込みは直接上書きでき、次のトランザクションで書き込まれた値を最終結果として使用できます。

写真

図2.4 上書き書き込み

上図のように、tx1とtx2の両方がk3に対して書き込み動作を実行しますが、図2.4左図のようにtx1が先に実行され、tx2が実行された場合、k3の値変更ロジックはv3->v3'->となります。 v3'' 、この実行プロセスは正しいです。右図のようにtx2が先に実行され、次にtx1が実行され、k3の値変更ロジックがv3→v3''→v3'であれば、この実行処理も正しいことになります。どちらの実行順序を使用しても、2 つのトランザクションの実行プロセスは論理的であり、正確性に問題はありません (どちらのトランザクションも k3 を読み込まず、読み書きの競合がないため)。以下を確保するだけで済みます。他のノードも同じ順序で実行されます。これも順序要件です。

3. 長安チェーンにおけるトランザクション競合検出スキーム

上記の分析後、同時実行時のトランザクション競合検出では、読み取り/書き込み競合を検出するだけで済み、すべてのトランザクションの実行中に読み取り/書き込み競合がなくなるまで、後で実行されるトランザクションを再実行する必要があることがわかります。

繰り返しになりますが、ここでのトランザクション競合検出は、読み取り/書き込み競合のあるトランザクションが特定の順序で実行され、スケジューリング実行プロセス中に正しい結果が得られることを保証するだけであり、正確性条件を満たすだけです。このノードがトランザクションを実行する順序を他のノードにどのように知らせるかは、順序条件によって保証されます。これは、後の 2 番目の記事で DAG を構築する責任になります。混同しないようにしてください。

写真

図 3.1 競合検出のフローチャート

長安チェーンでは、スケジューラ モジュールが各ブロックのスケジューリングと実行を担当します。各ブロックをスケジューリングして実行するとき、スケジューラは各ブロックのスナップショット構造を構築します。この構造には注目する必要がある 4 つの変数があります。 :

● ExecutedTxs: 実行されたトランザクションキューはスライス構造になっており、トランザクションが「正しく」実行されるたびにトランザクションがキューに追加され、キューインデックスは0から始まります。また、各トランザクションを処理して simContext を構築する際、現在のトランザクションの実行回数 txSeq は len(ExecutedTxs); によって計算されます。

● WriteTable: トランザクション書き込みセットの最新セット。マップ セットです。キーはトランザクション書き込みセット内のキー、値は書き込みセット キーと対応するトランザクションの txSeq に対応する値です。

● ReadTable: マップ セットでもあるトランザクション読み取りセットの最新セット。キーはトランザクション読み取りセット内のキー、値は読み取りセット キーと対応するトランザクションの txSeq に対応する値です。

● ロック: スナップショットの読み取り/書き込みロック。これにより、同時操作時のスナップショット内の変数の正確性が保証されます。

WriteTable と ReadTable は、ブロックが実行されるサンドボックス環境で変更および読み取られるホットスポット データとして、データベース内のデータとともに最新の「世界状態」を形成するものとして理解できます。

写真

図 3.2 スナップショットの 3 つのデータ構造

具体的なプロセス:

1. まず、ブロック内のトランザクションをチャネルチャネルに分散します。ここでのトランザクション分散にはいくつかの最適化があります。ガスアカウントまたは送信者に従ってグループ化でき、異なるグループ間の同時スケジューリングを可能な限り実装できます。読み取りと書き込みの競合を軽減します。状況についてはここでは詳しく説明しません。

2. 複数のゴルーチンがチャネルからトランザクションを同時にフェッチし、それらを並行して実行します。デフォルトの同時実行レベルは CPU コア数の 4 倍であるため、このブロック内の 4 つのトランザクションは完全に並行して実行されます。実装は次のとおりです。同時コルーチン プールは、トランザクションの競合率に応じて適応的に調整することもできますが、ここでは詳しく説明しません。

2.1 各トランザクションが実行のために vm を実際に呼び出す前に、スナップショットを通じて現在のトランザクションの実行番号 txSeq を取得する必要があります。これは SimContext の構築に使用されます。

2.2 この時点ではトランザクションはまだ実行されていないため、4 つのトランザクションで取得される txSeq はすべて 0 です。これは len(ExecutedTxs) によって計算されます。

3. 4 つのコルーチンが vm モジュールを並行して呼び出して 4 つのトランザクションを実行し、トランザクションごとに読み取り/書き込みセットを生成します。実際、VM がコントラクトを実行するとき、最初に WriteTable と ReadTable から読み取ります。 DB.値から読み取る必要があります。

4. vm モジュールは、読み取り/書き込みセットを含むトランザクション実行結果をスケジューリング モジュールに返します。

5. スケジューリングモジュールは、完了したトランザクションに対してトランザクション競合検出を実行します。次の 2 つの条件が同時に満たされた場合、トランザクションが競合していることを意味します。

● このトランザクションの読み取りセットのキー (k0 と仮定します) は WriteTable にあります。

● そして、このトランザクションの txSeq は、WriteTable の k0 に対応する txSeq 以下です。

6. トランザクションと以前に実行されたトランザクションの間に読み取り/書き込み競合がない場合は、トランザクションを ExecutedTxs キューに入れ、トランザクションの読み取り/書き込みセットと txSeq で WriteTable と ReadTable を更新します。

7. トランザクションと以前に実行されたトランザクションの間に読み取り/書き込みの競合がある場合、トランザクションは再スケジュールされて分散され、チャネル チャネルに入れられ、すべてのトランザクションが実行中にトランザクション競合検出を通過するまで再度実行されます。 。

以下では、tx0 と tx1 を例として、読み取り/書き込み競合を検出する方法を説明します。

写真

図3.3 競合検出の例図

上の左の図に示すように、この場合、読み取りと書き込みの競合はありません。tx0 と tx1 は並列かつインターリーブで実行されます。txSeq は同じですが、tx1 は tx0 より前に実行されます。tx1 の実行後、WriteTable はk3とtxSeqに対応する最新の値。tx0 の実行が完了すると、tx0 の読み取りセット k1 は WriteTable に存在せず、つまり、tx0 のトランザクション実行が依存する事前十分条件は破られておらず、tx0 の実行は正常です。時刻になったら、ExecutedTxs キューに tx0 を追加し、トランザクションの読み取りおよび書き込みセットを使用します。WriteTable と ReadTable を txSeq で更新するだけです。

この場合、上右図に示すように、読み取りと書き込みの競合が発生し、tx1 よりも先に tx0 が実行され、tx0 の実行後、k2 と txSeq に対応する最新の値が WriteTable にキャッシュされます。tx1 の実行が完了すると、tx1 の読み取りセット k2 が WriteTable にあり、tx1 の txSeq は 0 (WriteTable 内の k2 の最新の実行シーケンス 0 以下) になります。 tx1 のトランザクション実行が依存するトランザクションが破壊され、tx1 の実行が失敗するため、tx1 を再スケジュールして配布し、再度実行する必要があります。

写真

図3.4 競合後の再実行

tx1 が再実行されると、このトランザクションのみが実行されると仮定すると、txSeq の値は len(ExecutedTxs) に対して 1 になり、VM がトランザクションを実行するときに、スナップショットの WriteTable 値と ReadTable 値から k2 を優先的に読み取ります。このとき読み出した結果は最新のv2'です。tx1を実行すると、k2はWriteTableにありますが、そのtxSeqがWriteTableのtxSeq 0より1大きいことが分かり、競合はありません。tx1をExecutedTxsキューに追加します。そして、トランザクションの読み取り/書き込みセットと txSeq を使用して WriteTable と ReadTable を更新します。

4. 結論

ここまで長安チェーンにおけるトランザクションのスケジューリングとトランザクションの競合検出のプロセスと仕組みを紹介しましたが、この際、トランザクション実行プロセスの正確性が保証されていますが、次の記事では長安チェーンがどのようにして秩序ある実行を実現しているのかについて紹介していきます。取引の。

おすすめ

転載: blog.csdn.net/weixin_55760491/article/details/132564473