8.1 トランザクションID
XID または TxID として指定されます (違いがある場合は教えてください)。タイムスタンプは TxID として使用でき、すべてのアクションをある時点まで復元したい場合に影響を与える可能性があります。タイムスタンプが十分に細分化されていない場合、トランザクションが同じ ID を取得する可能性があるため、問題が発生する可能性があります。
したがって、最も信頼できるオプションは、一意の UUID 製品 ID を生成することです。Python ではこれは非常に簡単です。
>>> import uuid
>>> str(uuid.uuid4())
'f50ec0b7-f960-400d-91f0-c42a6d44e3d0'
>>> str(uuid.uuid4())
'd15bed89-c0a5-4a72-98d9-5507ea7bc0ba'
トランザクション定義データのセットをハッシュし、このハッシュを TxID として使用するオプションもあります。
8.2 再試行
特定の関数またはプログラムが冪等であることがわかっている場合、これは、エラーが発生した場合にその呼び出しを繰り返すことができる、またそうすべきであることを意味します。そして、一部の操作でエラーが発生するという事実に備えておく必要があります。最新のアプリケーションがネットワークとハードウェア上に分散されていることを考えると、エラーは例外ではなく標準として考慮される必要があります。このエラーは、サーバーのクラッシュ、ネットワーク エラー、リモート アプリケーションの輻輳によって発生する可能性があります。アプリケーションはどのように動作すべきでしょうか? そうです、操作を繰り返してみてください。
1 つのコードでページ全体の単語以上のことが言えるため、単純な再試行メカニズムが理想的にどのように機能するかを理解するために 1 つの例を使用してみましょう。Tenacity ライブラリを使用してこれを示します (このライブラリは非常によく設計されているため、使用する予定がなくても、この例で反復メカニズムを設計する方法が示されます)。
import logging
import random
import sys
from tenacity import retry, stop_after_attempt, stop_after_delay, wait_exponential, retry_if_exception_type, before_log
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
logger = logging.getLogger(__name__)
@retry(
stop=(stop_after_delay(10) | stop_after_attempt(5)),
wait=wait_exponential(multiplier=1, min=4, max=10),
retry=retry_if_exception_type(IOError),
before=before_log(logger, logging.DEBUG)
)
def do_something_unreliable():
if random.randint(0, 10) > 1:
raise IOError("Broken sauce, everything is hosed!!!111one")
else:
return "Awesome sauce!"
print(do_something_unreliable.retry.statistics)
> 念のため言っておきますが、 \@retry(...) は「デコレータ」と呼ばれる特別な Python 構文です。これは、別の関数をラップし、実行前または実行後に何かを行う単なる retry(...) 関数です。
ご覧のとおり、再試行は創造的に設計できます。
- 試行を時間 (10 秒) または試行回数 (5 回) で制限できます。
- 指数関数的になることもあります (つまり、2 ** 増加する数 n )。または、別の方法 (たとえば、修正) で、別々の試行間の時間を延長します。指数関数的な変化は「輻輳崩壊」と呼ばれます。
- 特定のタイプのエラー (IOError) に対してのみ再試行できます。
- 再試行は、ログ内のいくつかの特別なエントリによって先行または完了される場合があります。
若手戦士コースを完了し、アプリケーション側でトランザクションを操作するために必要な基本的な構成要素を理解したので、分散システムでトランザクションを実装できる 2 つの方法について学びましょう。
8.3 トランザクション愛好家のための高度なツール
このトピックは別の大きな記事に値するため、かなり一般的な定義のみを説明します。
2 フェーズ コミット (2pc)。2pc には、準備フェーズとコミットフェーズの 2 つのフェーズがあります。準備フェーズでは、すべてのマイクロサービスは、アトミックに実行できるいくつかのデータ変更を準備するように求められます。すべての準備が完了したら、コミットフェーズで実際の変更を加えます。プロセスを調整するには、必要なオブジェクトをロックするグローバル コーディネーターが必要です。つまり、コーディネーターがロックを解除するまで、オブジェクトにアクセスして変更することはできなくなります。特定のマイクロサービスが変更に対応する準備ができていない場合 (応答しない場合など)、コーディネーターはトランザクションを中止し、ロールバック プロセスを開始します。
なぜこのプロトコルが優れているのでしょうか? それは原子性を提供します。さらに、書き込み時と読み取り時の分離を保証します。これは、あるトランザクションへの変更は、コーディネーターが変更をコミットするまで他のトランザクションには表示されないことを意味します。ただし、これらのプロパティには欠点もあります。このプロトコルは同期 (ブロッキング) であるため、(RPC 呼び出し自体は非常に遅いにもかかわらず) システムの速度が低下します。また、相互ブロックの危険性があります。
佐賀。このパターンでは、分散トランザクションは、関連するすべてのマイクロサービスにわたる非同期ローカル トランザクションによって実行されます。マイクロサービスはイベント バスを介して相互に通信します。いずれかのマイクロサービスがローカル トランザクションを完了できなかった場合、他のマイクロサービスが補償トランザクションを実行して変更をロールバックします。
Saga の利点は、障害物がないことです。しかし、もちろん欠点もあります。
Saga は、特に多数のマイクロサービスが関係している場合、デバッグが困難です。Saga パターンのもう 1 つの欠点は、読み取り分離が欠けていることです。つまり、ACID で示されるプロパティが私たちにとって重要である場合、Saga は私たちにはあまり適していません。
これら 2 つのテクニックの説明から何がわかるでしょうか? 分散システムでは、原子性と分離に対する責任はアプリケーションにあるという事実。ACID 保証を提供しないデータベースを使用する場合も同じことが発生します。つまり、競合の解決、ロールバック、コミット、スペースの解放などは開発者の肩にかかっています。
8.4 ACID 保証が必要な場合はどうすればわかりますか?
特定のユーザーまたはプロセスのセットが同時に同じデータを処理する可能性が高い場合。
ありきたりな話で恐縮ですが、典型的な例は金融取引です。
トランザクションの実行順序が重要な場合。
あなたの会社が FunnyYellowChat メッセンジャーから FunnyRedChat メッセンジャーに切り替えようとしていると想像してください。FunnyRedChat では GIF を送信できますが、FunnyYellowChat では送信できないからです。しかし、単にメッセンジャーを変更するだけではなく、会社の通信をあるメッセンジャーから別のメッセンジャーに移行することになります。これは、プログラマがプログラムやプロセスを一元的に文書化するのが面倒で、代わりにメッセンジャーの別のチャネルにすべてを公開したためです。はい、営業担当者は交渉と合意の詳細を同じ場所で公開しました。つまり、会社の全活動がそこにあり、すべてを文書化するサービスに転送する時間は誰もなく、インスタント メッセンジャーの検索はうまく機能するため、瓦礫を片付ける代わりに、単にすべての文書をコピーすることに決めたのです。新しい場所にメッセージを送ります。メッセージの順序は重要です
ちなみに、メッセンジャーでのやり取りの場合、一般に順序は重要ですが、2人が同じチャットに同時に何かを書き込む場合、一般に誰のメッセージが最初に表示されるかはそれほど重要ではありません。したがって、この特定のシナリオでは、ACID は必要ありません。
考えられるもう 1 つの例はバイオインフォマティクスです。全く分かりませんが、ヒトゲノムを解読するには順序が重要なのではないでしょうか。しかし、バイオインフォマティシャンは通常、あらゆることに何らかのツールを使用していると聞きました。おそらく独自のデータベースを持っているのでしょう。
ユーザーに提供できない、または古いデータを処理できない場合。
そして再び金融取引です。正直に言うと、他に例が思いつきませんでした。
保留中のトランザクションに多大なコストが伴う場合。データベースではトランザクションを分離できないため、医師と看護師が両方とも患者記録を更新し、同時に互いの変更を消去した場合に発生する可能性のある問題を想像してみてください。医療システムは、金融のほかに、ACID の保証が重要となる傾向にあるもう 1 つの分野です。
8.5 ACID が必要ないのはどのような場合ですか?
ユーザーがプライベート データの一部のみを更新する場合。
たとえば、ユーザーは Web ページにコメントや付箋を残します。または、サービスプロバイダーの個人アカウント内の個人データを編集します。
ユーザーがデータをまったく更新せず、新しいデータを追加するだけ(追加)する場合。
たとえば、ランニングに関するデータ (どれくらい走ったのか、どのくらいの時間、ルートなど) を保存するランニング アプリケーションです。新しい実行はそれぞれ新しいデータであり、古いデータはまったく編集されません。おそらく、データに基づいて分析を行うことができます。このシナリオには NoSQL データベースだけが適しています。
ビジネス ロジックにより、トランザクションを実行する特定の順序の必要性が判断されない場合。
おそらく、次の生放送中に新しい素材を制作するために寄付を集める Youtube ブロガーにとって、誰が、いつ、どのような順序でお金を投げ込んだかはそれほど重要ではありません。
ユーザーが同じ Web ページまたはアプリケーション ウィンドウに数秒または数分滞在するため、何らかの理由で古いデータが表示される場合。
理論的には、これらはオンライン ニュース メディア、または同じ Youtube です。あるいは「ハブル」。 未完了のトランザクションがシステムに一時的に保存されても問題ない場合は、無視しても問題はありません。
多くのソースからデータを集約し、高頻度で更新されるデータ (たとえば、少なくとも 5 分ごとに変化する都市の駐車スペースの占有率に関するデータ) を集約している場合、理論的には大きな問題にはなりません。ある時点でいずれかの駐車場の取引が完了しなかった場合に備えて。もちろん、それはこのデータを正確に何をしたいかによって異なります。
GO TO FULL VERSION