ログ処理の範囲は、リアルタイムコンピューティング、データウェアハウス、オフラインコンピューティングなど、広範囲にわたります。 ここでは、アップストリームとダウンストリームのビジネスシステムに信頼性の問題 (障害の発生など) がある場合や、ビジネストラフィックが変動する場合でも、リアルタイムコンピューティングシナリオでデータの損失や繰り返しなく、ログを順番に処理する方法について説明します。

わかりやすく説明するために、ここでは、ログの処理方法を銀行での 1 日に例えて説明します。 加えて、Log Service の LogHub 機能も紹介します。これに基づいて、Spark Streaming および Storm のスパウトを使用してログを処理できます。

ログデータとは

LinkedIn 社の元社員である Jay Kreps 氏は、ログを、追加のみが可能な、時系列で完全に順序付けられたレコードシーケンスと定義しています。(参照:The Log: What every software engineer should know about real-time data's unifying abstraction)

  • 追加のみ可能:ログエントリはログの最後方に追加されます。 これらは生成された後、変更することはできません。
  • 時系列による完全な順序付け:ログエントリは厳密に順序付けられています。 各ログエントリには、タイムスタンプを示す、一意の連続したログエントリ番号が割り当てられます。 同じタイムスタンプ (秒単位) で異なるログエントリが生成される場合があります。 たとえば、GET 操作と SET 操作は同じタイムスタンプで実行されます。 ただし、コンピューター上では、これらの 2 つの操作は順に実行されます。

どのようなデータをログに抽象化できますか。

半世紀前は、船長と操船者は厚いノートでログを記録していました。 現代では、コンピューターであらゆる場所からログを生成および取得することができます。 サーバーやルーター、センサー、GPS、順序、多種多様なデバイスが私たちの生活をさまざまな観点から表しています。 船長は、ログの時間を記録するために使用されるタイムスタンプに加え、テキスト、画像、気象条件、航行方向など、必要な情報をすべてログに記録していました。 半世紀経った現在、順序、支払記録、ユーザーアクセス記録、データベース操作など、さまざまなシナリオにおいてログが生成されます。

コンピューター分野では、基本的なログには、メトリック、リレーショナルデータベースと NoSQL データベースのバイナリログ、イベント、監査ログ、アクセスログが含まれます。

ここでは、銀行でのユーザー操作をログエントリとして説明します。 これには、ユーザーの名前、アカウント、操作時間、操作タイプ、トランザクション量が含まれます。

例:

2016-06-28 08:00:00 Zhang San Deposit RMB 1,000
2016-06-28 08:00:00 Zhang San Deposit RMB 1,000

LogHub データモデル

抽象概念をわかりやすく説明するため、このセクションでは、Alibaba Cloud Log Service の LogHub データモデルを使用してデモンストレーションします。 詳細については、Log Service のプロダクト紹介の「概要」をご参照ください。

  • ログは、キーとバリューのペアのグループ、および時間で構成されます。
  • ロググループは、IP アドレスやソースなどの同じメタデータを持つログの集まりです。

次の図は、ログとロググループの関係を示します。

  • シャードは、ロググループの基本的な読み書きユニットです。 これは 48 時間の FIFO (先入れ先出し) キューと考えることができます。 各シャードでは、5 MB/秒でデータを書き込み、10 MB/秒でデータを読み取ることができます。シャードの論理範囲は、BeginKey と EndKey によって指定されます。 この範囲により、ひとつのシャードに、別のシャードと異なるタイプのデータを含めることができます。
  • Logstore には、同じタイプのログデータが格納されます。 各 Logstore は、範囲が [0000, FFFF..) である 1 つ以上のシャードで構成されるキャリアです。
  • プロジェクトは、Logstore のコンテナを指します。

次の図は、ログ、ロググループ、シャード、Logstore、プロジェクト間の関係を示します。

銀行の 1 日

19 世紀で、都市の複数のユーザーが銀行に行って預金または引き落としをしたとします。 銀行には複数の銀行員がいます。 当時は、リアルタイムでデータを同期できるコンピューターは存在しませんでした。 各銀行員が口座帳簿にデータを記録し、口座帳簿を使用して銀行にある資金を毎晩確認していました。 このケースでは、ユーザーがデータの作成者であり、預金と引き落としはユーザーの操作とされ、銀行員がデータの取得者となります。

分散されたログ処理システムでは、銀行員が固定記憶装置と計算機能を備えたスタンドアロンサーバーであり、ユーザーが各種データソースから送られる要求、銀行がユーザーがデータの読み書きをする Logstore となります。

  • ログ、ロググループ:預金や引き落としなどのユーザー操作。
  • ユーザー:操作の実行者。
  • 銀行員:銀行でユーザーの要求を処理する従業員。
  • 銀行 (Logstore):ユーザーからの要求を受信し、処理を行う銀行員に割り当てる場所。
  • シャード:銀行でマネージャーがユーザーの要求をソーティングする方法。

1 つ目の課題:順序

銀行に 2 人の銀行員 (A と B) がいるとします。 Zhang San さんが銀行へ行き、カウンター A で RMB 1,000 を預金します。銀行員 A は、 預金出納帳 A に取引額を記録します。午後、Zhang San さんはさらにお金が必要になったため、カウンター B で引き落としをします。 カウンター B で銀行員 B が預金出納帳 B を確認したところ、預金の記録がありません。

この例の場合、預金と引き落としには厳密な順序付けが必要です。 ユーザー操作のステータスの一貫性を確保するため、同じユーザーからの要求は同じ銀行員が処理する必要があります。

確実に順序付けがされるようにするには、ユーザーは順に列に並んで要求を送ることができます。 FIFO の原則に基づいて、銀行員 A のみにユーザーの要求が割り当てられるよう、シャードを作成します。 ただし、この方法では、1,000 人のユーザーからの要求を 10 人の銀行員に割り当てたとしても非効率であると言えます。

このシナリオでは、次のソリューションを使用して効率を改善することができます。

  1. 10 人の銀行員に 10 個のシャードを作成します。 各シャードの担当者を割り当てます。
  2. 同じ口座に対する操作が確実に順序付けられるようにします。一貫したハッシュを使用してユーザーをマッピングします。 たとえば、銀行口座またはユーザー名でユーザーを特定のシャードにマッピングします。 この場合、hash(Zhang) = Z の方程式を使用して、 Zhang San さんからの要求は範囲に Z が含まれる特定のシャードに常にマッピングされます。たとえば、1 人の銀行員 (例:クラークA) にこのシャードの要求が割り当てられます。

Zhang という名字を持つユーザーが複数存在する場合は、ソリューションを調整することができます。 たとえば、ハッシュ関数を使用して、アカウント ID または郵便番号でユーザーをシャードにマッピングし、ユーザーの要求を各シャードにより均等に分散できるようにします。

2 つ目の課題:最低 1 回

Zhang San さんがカウンター A で預金したとします。この預金の要求の処理中に銀行員 A は電話を受けました。 通話終了後、銀行員 A は Zhang San さんの預金の要求が処理されたと誤って判断し、次のユーザーからの要求の処理を開始しました。 この場合、Zhang San さんの預金の要求は記録されません。

コンピューターではこの場合の銀行員の間違いのようなことが起こることはなく、コンピューターはより長期間にわたってより確実に動作します。 ただし、コンピューターは、障害や過負荷などの理由によりデータの処理に失敗する場合があります。 このような理由による預金の損失は許容されません。

このシナリオでは、次のソリューションを使用してデータの損失を回避することができます。

銀行員 A は、シャード内の現在の要求の位置を要求の進行状況としてノート (口座帳簿 Aとは異なる) に記録します。 この場合、銀行員 A は、Zhang San さんの預金の要求が処理されてから、次のユーザーを受け付けます。

ただし、このソリューションでは繰り返しが生じる可能性があります。 たとえば、Zhang San さんの預金の要求を処理して口座帳簿 A のデータを更新した後、銀行員 A は離席しましたが、現行の要求の位置をノートブックのシャードに記録しませんでした。 銀行員 A が席に戻り Zhang San さんの要求の処理状況をノートで確認できなかった場合、同様の要求の処理を繰り返す可能性が生じます。

3 つ目の課題:1 回のみ

繰り返しが生じた場合必ず問題になるかというと、 必ずとは限りません。

べき等操作を複数回実行すると、時間とエネルギーを浪費する可能性があります。 ただし、このような繰り返し作業の場合は結果に影響しません。 たとえば、残高照会は、ユーザーが実行する読み取り専用操作です。 この操作を繰り返しても、照会結果には影響しません。 読み取り専用でない操作 (ユーザーのログオフなど) でも、連続して 2 回実行できる操作もあります。

しかし実際は、ほとんどの操作 (入出金など) はべき等ではありません。 これらの操作を繰り返し実行すると、結果に致命的な影響が及ぶ可能性があります。 このような繰り返しを回避するためにはどのようなソリューションがあるでしょうか。 銀行員 A はユーザーの要求を処理した後、口座帳簿 A のデータを更新し、シャードの現在の要求の位置をノートに記録し、2 つのレコードをチェックポイントに結合する必要があります。

銀行員 A が一時的または永久的にいなくなった場合、他の銀行員が次のような工程で操作を続行することができます:現在のユーザーの要求のチェックポイントが存在する場合は、次のユーザーの要求に進みます。 現在のユーザーの要求のチェックポイントが存在しない場合はその要求を処理します。 こうして操作の原子性を確保します。

チェックポイントは、シャード内の要素の位置または時間をキーとして記録し、要素が処理されたことを示すことができる、永続的なオブジェクトです。

ビジネスにおける課題

ここでの原則は複雑なものではありません。 しかし、現実の世界では変化と不確実性が存在するためにこれら 3 つの課題が複雑化します。 例:

  1. 給料日にはユーザー数が急増します。
  2. コンピューターとは異なり、銀行員は休憩やランチをとる必要があります。
  3. サービスの品質を向上させるためには、マネージャーが銀行員に適切なタイミングで迅速に対応するよう指導する必要があります。 マネージャーは、シャードでの要求の処理の速度に応じて適切なタイミングを判断できるでしょうか。
  4. 銀行員は、引渡し中に口座帳簿とチェックポイントを簡単かつ適切に転送する必要があります。

現実ビジネスにおける 1 日

08:00 銀行が営業を開始

すべてのユーザーからの要求は、唯一のシャードであるシャード 0 に割り当てられます。 銀行員 A にこの要求を処理する責任があります。

10:00 以降のピーク時間

銀行のマネージャーは、10:00 以降にシャード 0 をシャード 1 とシャード 2 に分割することにしました。 その際、マネージャーは次のルールに基づいて 2 つのシャードにユーザーの要求を割り当てます:ユーザーの姓の先頭文字列が A〜W の要求はシャード 1 に割り当てられます。 ユーザーの姓の先頭文字列が X または Y または Z の要求はシャード 2 に割り当てられます。 2 つのシャードの範囲が異なる理由は、ほとんどの姓が X または Y または Z で始まるためです。このマッピング方法により、ワークロードを平等に保つことができます。

次の図は、10:00 から12:00 までのシャード内のユーザーの要求のステータスを示します。

銀行員 A が 2 つのシャードで要求を処理するのが困難な場合、マネージャーが銀行員 B と銀行員 C を配置します。銀行員 B がシャードの 1 つを引き継ぎ、銀行員 C がアイドル状態になります。

12:00 ユーザー数がさらに増加

マネージャーは、シャード 1 の要求を処理している銀行員 A の負荷が大きいと判断し、シャード 1 をシャード 3 とシャード 4 に分割します。 銀行員 A はシャード 3 の要求を処理し、銀行員 C はシャード 4 の要求を処理します。 12:00 以降、マネージャーは、当初シャード 1 に割り当てられていたユーザーの要求をシャード 3 とシャード 4 に割り当てます。

次の図では、12:00 以降のシャード内のユーザーの要求のステータスを示します。

16:00 以降ユーザー数が減少

マネージャーは、銀行員 A と銀行員 B に休憩をとるよう指示し、銀行員 C にシャード 2、シャード 3、シャード 4 の要求を割り当てます。 その後、マネージャーはシャード 2 とシャード 3 を結合してシャード 5 を作成し、シャード 5 とシャード 4 を結合してシャード 6 を作成します。 シャード 6 のすべてのユーザーの要求が処理された後、銀行は営業を終了します。

実際のログ処理

前述のプロセスは、一般的なログ処理シナリオに抽象化できます。 銀行の業務要件を満たすため、オートスケーリングと柔軟なログフレームワークを活用することで、次の機能が利用可能になります。

  1. シャードを自動的にスケールインまたはスケールアウトします。
  2. コンシューマーグループにコンシューマーが追加または削除されたときに、そのコンシューマーグループのコンシューマーにシャードを自動的に適合させます。 このプロセスでデータの整合性を保証し、ログを順番に処理します。
  3. ログは一度のみ処理されるため、コンシューマーとの協力が必要になります。
  4. 取得状況をモニタリングし、コンピューティングリソースを適切に割り当てます。
  5. より多くのソースからのログに対応できます。 銀行の場合、ユーザーはオンラインバンキング、モバイルバンキング、小切手などのさまざまなチャネルから要求を送信できます。

一般的なシナリオでは、LogHub と LogHub のコンシューマーライブラリを使用してリアルタイムでログを処理できます。 コンシューマーライブラリを使用すると、トラフィックのスケーリングやフェールオーバーの心配をする必要がなく、ビジネスロジックに注力できます。