リアルタイムメータリングアプリケーションにRedisを使用する方法

Roshan Kumarは、RedisLabsのシニアプロダクトマネージャーです。

メータリングは単なるカウントの問題ではありません。計測はしばしば測定と混同されますが、通常はそれ以上です。計測には測定が含まれますが、進行中のプロセスとして、通常はリソースの使用量またはフローを時間の経過とともに調整することを目的としています。最新のアプリケーションには、人、オブジェクト、またはイベントのカウントから、使用法の規制、アクセスの制御、容量の割り当てまで、さまざまな方法でメータリングが組み込まれています。

メータリングソリューションは通常、厳しいパフォーマンス要件を満たしながら、大量のデータを処理する必要があります。ソリューションの規模によっては、カウントとメータリングには、毎秒数百万とまではいかなくても数千のデータベースの更新が含まれる場合があります。このようなソリューションをサポートするためのデータベースの主な要件は、書き込み操作のスループットが高く、応答の待ち時間が短い(サブミリ秒)ことです。

オープンソースのインメモリデータベースプラットフォームであるRedisは、最小限のハードウェアリソースを使用するという点で費用効果が高いと同時に、これらの両方の利点を提供します。この記事では、計量ソリューションに適したRedisの特定の機能と、その目的でRedisを使用する方法について説明します。しかし、最初に、メータリングのより一般的な使用法のいくつかを見てみましょう。

一般的なメータリングアプリケーション

時間の経過とともにリソースの使用を測定する必要があるアプリケーションでは、メータリングが必要です。一般的なシナリオは次の4つです。

  1. 消費ベースの価格設定モデル。1回限りの支払いモデルやサブスクリプションベースの支払いモデルとは異なり、消費ベースの価格設定モデルでは、消費者は実際の使用量に対してのみ支払うことができます。消費者はより大きな柔軟性、自由、およびコスト削減を享受し、プロバイダーはより大きな消費者維持を獲得します。

    このようなモデルの実装には注意が必要です。メータリングシステムは、単一のプランで多くの使用項目と多くのメトリックを追跡する必要がある場合があります。たとえば、クラウドプロバイダーは、CPUサイクル、ストレージ、スループット、ノード数、またはサービスの使用時間にさまざまな価格レベルを設定できます。電気通信会社は、分、データ、またはテキストに対してさまざまなレベルの許容消費量を設定する場合があります。メータリングソリューションは、消費ベースの価格設定のタイプに応じて、上限、課金、または拡張サービスを実施する必要があります。

  2. リソース使用率の制限。インターネット上のすべてのサービスは、そのサービスがレート制限されていない限り、過度の使用によって悪用される可能性があります。このため、Google AdWordsAPIやTwitterStreamAPIなどの人気のあるサービスにはレート制限が組み込まれています。極端な悪用の場合、サービス拒否(DoS)につながります。悪用を防ぐために、インターネット上でアクセス可能なサービスとソリューションは、適切なレート制限ルールを使用して設計する必要があります。単純な認証ページとログインページでさえ、特定の時間間隔での再試行回数を制限する必要があります。

    リソース使用率の制限が必要になるもう1つの例は、ビジネス要件の変更により、レガシーシステムがサポートできるよりも大きな負荷がかかる場合です。レガシーシステムへの呼び出しをレート制限することで、企業はレガシーシステムを交換することなく増大する需要に適応できます。

    悪用を防ぎ、負荷を軽減することに加えて、適切なレート制限は、バースト性のあるトラフィックシナリオの管理にも役立ちます。たとえば、ブルートフォースレート制限メソッドを適用するAPIでは、1時間あたり1000回の呼び出しが許可される場合があります。トラフィックシェーピングポリシーが設定されていない場合、クライアントは1時間ごとの最初の数秒間にAPIを1000回呼び出す可能性があり、インフラストラクチャがサポートできる範囲を超える可能性があります。 TokenBucketやLeakyBucketなどの一般的なレート制限アルゴリズムは、通話を制限するだけでなく、時間の経過とともに配信することでバーストを防ぎます。

  3. リソースの分散。輻輳と遅延は、パケットルーティング、ジョブ管理、トラフィック輻輳、クラウド制御、ソーシャルメディアメッセージング、データ収集などを処理するアプリケーションの一般的なシナリオです。キューイングモデルには、到着率と出発率に基づいてキューサイズを管理するためのいくつかのオプションがありますが、これらのモデルを大規模に実装することは容易ではありません。

    高速データストリームを処理する場合、バックログと輻輳は常に心配です。賢い設計者は、キューのパフォーマンスの監視とキューのサイズに基づく動的ルーティングの両方を取り入れながら、許容可能なキューの長さの制限を定義する必要があります。

  4. リアルタイムの意思決定のために大規模にカウントします。Eコマースサイト、ゲームアプリケーション、ソーシャルメディア、モバイルアプリは、毎日何百万人ものユーザーを魅了しています。より多くの眼球がより多くの収入を生み出すので、訪問者と彼らの行動を正確に数えることはビジネスにとって重要です。カウントは、エラーの再試行、問題のエスカレーション、DDoS攻撃の防止、トラフィックプロファイリング、オンデマンドのリソース割り当て、不正の軽減などのユースケースにも同様に役立ちます。

メータリング設計の課題

ソリューションアーキテクトは、メータリングアプリケーションを構築する際に、次の4つから始めて多くのパラメータを考慮する必要があります。

  1. 設計の複雑さ。データの量を数え、追跡し、調整することは、特にデータが高速で到着したときに、困難な作業です。ソリューションアーキテクトは、プログラミング言語構造を使用して、アプリケーション層でメータリングを処理できます。ただし、このような設計は、障害やデータ損失に対して耐性がありません。従来のディスクベースのデータベースは堅牢であり、障害発生時の高度なデータ耐久性を約束します。ただし、必要なパフォーマンスを提供するには不十分であるだけでなく、メータリングを実装するための適切なデータ構造とツールがないと複雑さが増します。
  2. レイテンシー。メータリングには通常、カウントの多数の継続的な更新が含まれます。ネットワークとディスクの読み取り/書き込みの待ち時間は、多数を処理するときに加算されます。これにより、大量のデータのバックログが蓄積され、遅延が増える可能性があります。レイテンシのもう1つの原因は、データベースからプログラムのメインメモリにメータリングデータをロードし、カウンタの更新が完了するとデータベースに書き戻すプログラム設計です。
  3. 並行性と一貫性。数百万、数十億のアイテムをカウントするソリューションの設計は、イベントがさまざまな地域でキャプチャされると複雑になる可能性があり、それらすべてを1か所に収束させる必要があります。多くのプロセスまたはスレッドが同じカウントを同時に更新している場合、データの整合性が問題になります。ロック技術は、一貫性の問題を回避し、トランザクションレベルの一貫性を提供しますが、ソリューションの速度を低下させます。
  4. 耐久性。メータリングは収益数に影響します。これは、一時的なデータベースが耐久性の観点から理想的ではないことを意味します。耐久性オプションを備えたインメモリデータストアは完璧な選択です。

メータリングアプリケーションにRedisを使用する

次のセクションでは、Redisを使用してソリューションをカウントおよび計測する方法を検討します。Redisには、データ構造、アトミックコマンド、およびTime-to-Live(TTL)機能が組み込まれており、これらを使用して計測のユースケースに電力を供給することができます。Redisは単一のスレッドで実行されます。したがって、すべてのデータベース更新がシリアル化され、Redisがロックフリーのデータストアとして実行できるようになります。これにより、開発者はスレッドの同期やデータ整合性のためのロックメカニズムの実装に労力を費やす必要がないため、アプリケーションの設計が簡素化されます。  

カウントするためのAtomicRedisコマンド

Redisは、アプリケーションのメインメモリに値を読み取る必要なしに値をインクリメントするコマンドを提供します。

コマンド 説明
INCR キー キーの整数値を1つインクリメントします
INCRBY キーの増分 キーの整数値を指定された数だけインクリメントします
INCRBYFLOAT キーの増分 キーのfloat値を指定された量だけインクリメントします
DECR キー キーの整数値を1つデクリメントします
DECRBY キーデクリメント キーの整数値を指定された数だけデクリメントします
HINCRBY キーフィールドの増分 ハッシュフィールドの整数値を指定された数だけインクリメントします
HINCRBYFLOAT キーフィールドの増分 ハッシュフィールドのfloat値を指定された量だけインクリメントします

Redisは、整数を基数10の64ビット符号付き整数として格納します。したがって、整数の最大制限は非常に大きな数です:263 – 1 = 9,223,372,036,854,775,807。

Redisキーに組み込まれている存続可能時間(TTL)

メータリングの一般的な使用例の1つは、時間に対する使用状況を追跡し、時間がなくなった後にリソースを制限することです。Redisでは、キーの存続可能時間の値を設定できます。設定されたタイムアウト後、Redisは自動的にキーを無効にします。次の表に、キーを期限切れにするいくつかの方法を示します。

コマンド 説明
EXPIRE キー秒 キーの存続時間を秒単位で設定します
EXPIREAT キータイムスタンプ キーの有効期限をUnixタイムスタンプとして設定します
PEXPIRE キーミリ秒 キーの存続時間をミリ秒単位で設定します
PEXPIREAT キータイムスタンプ キーの有効期限をUNIXタイムスタンプとしてミリ秒単位で設定します
SET キー値[EX秒] [PXミリ秒] 文字列値を、オプションの存続時間とともにキーに設定します

以下のメッセージは、キーの存続時間を秒とミリ秒で示しています。

コマンド 説明
TTL キー キーのために生きる時間を得る
PTTL キー キーの存続時間をミリ秒単位で取得します

効率的なカウントのためのRedisデータ構造とコマンド

Redisは、リスト、セット、ソートされたセット、ハッシュ、ハイパーログログなどのデータ構造で愛されています。RedisモジュールAPIを介してさらに多くを追加できます。

Redisラボ

Redisデータ構造には、メモリ内(データが格納されている場所)で最大の効率で実行するように最適化された組み込みコマンドが付属しています。一部のデータ構造は、オブジェクトのカウント以上のことを達成するのに役立ちます。たとえば、Setデータ構造は、すべての要素の一意性を保証します。

並べ替えられたセットは、一意の要素のみがセットに追加されるようにし、スコアに基づいて要素を並べ替えることができるようにすることで、さらに一歩進んでいます。たとえば、並べ替えられたセットのデータ構造で要素を時間順に並べると、時系列データベースが提供されます。Redisコマンドを使用すると、要素を特定の順序で取得したり、不要になったアイテムを削除したりできます。

Hyperloglogは、オブジェクト自体を格納したりメモリに影響を与えたりすることなく、数百万の一意のアイテムの数を推定する別の特別なデータ構造です。

データ構造 コマンド 説明
リスト LLEN キー リストの長さを取得する
セットする SCARD キー セット内のメンバーの数を取得します(カーディナリティ)
ソートされたセット ZCARD キー ソートされたセットのメンバー数を取得します
ソートされたセット ZLEXCOUNT キー最小最大 指定された辞書式範囲の間でソートされたセット内のメンバーの数をカウントします
ハッシュ HLEN キー ハッシュ内のフィールド数を取得する
ハイパーログログ PFCOUNT キー Hyperloglogデータ構造によって観測されたセットのおおよそのカーディナリティを取得します
ビットマップ BITCOUNT キー[開始終了] 文字列内の設定ビットをカウントします

Redisの永続性とメモリ内レプリケーション

支払いなどの計測のユースケースには、ビジネスにとって重要な情報の保存と更新が含まれます。データの損失は、収益に直接影響します。また、コンプライアンスやガバナンスの要件であることが多い請求記録を破壊する可能性もあります。

データ要件に基づいて、Redisの一貫性と耐久性を調整できます。メータリングデータの永続的な記録証明が必要な場合は、Redisの永続化機能によって耐久性を実現できます。Redisは、書き込みコマンドが発生したときにディスクにコピーするAOF(追加専用ファイル)と、ある時点で存在するデータを取得してディスクに書き込むスナップショットをサポートしています。

組み込みのロックフリーRedisアーキテクチャ

Redis処理はシングルスレッドです。これにより、すべての書き込みコマンドが自動的にシリアル化されるため、データの整合性が保証されます。このアーキテクチャにより、開発者とアーキテクトは、マルチスレッド環境でスレッドを同期する負担から解放されます。

人気のある消費者向けモバイルアプリケーションの場合、数千、時には数百万のユーザーが同時にアプリケーションにアクセスしている可能性があります。アプリケーションが使用時間を計測していて、2人以上のユーザーが同時に分を共有できるとしましょう。並列スレッドは、データの整合性を確保するという追加の負担を課すことなく、同じオブジェクトを更新できます。これにより、速度と効率を確保しながら、アプリケーション設計の複雑さが軽減されます。

Redisメータリングサンプルの実装

サンプルコードを見てみましょう。以下のシナリオのいくつかは、使用されるデータベースがRedisでない場合、非常に複雑な実装を必要とします。

複数のログイン試行のブロック

アカウントへの不正アクセスを防ぐために、Webサイトは、ユーザーが規定の期間内に複数のログインを試行することをブロックする場合があります。この例では、単純なキーの存続可能時間機能を使用して、ユーザーが1時間に3回を超えるログイン試行を行うことを制限しています。

ログイン試行回数を保持するためのキー:

user_login_attempts:

手順:

現在の試行回数を取得します。

GET user_login_attempts:

nullの場合、有効期限を秒単位で設定します(1時間= 3600秒)。

SET user_login_attempts:1 3600

nullでなく、カウントが3より大きい場合は、エラーをスローします。

nullでない場合、およびカウントが3以下の場合は、カウントをインクリメントします。

INCR user_login_attempts:

ログインに成功すると、次のようにキーが削除される場合があります。

DEL user_login_attempts:

使った分だけ

Redis Hashデータ構造は、使用状況と請求を追跡するための簡単なコマンドを提供します。この例では、以下に示すように、すべての顧客の請求データがハッシュに保存されていると仮定します。

customer_billing:

     使用法

     費用

     。

     。

各ユニットのコストが2セントで、ユーザーが20ユニットを消費したとします。使用量と請求を更新するコマンドは次のとおりです。

hincrbyのお客様:使用法20

hincrbyfloatのお客様:コスト.40

お気づきかもしれませんが、アプリケーションは、データベースから独自のメモリにデータをロードしなくても、データベース内の情報を更新できます。さらに、オブジェクト全体を読み取らずに、ハッシュオブジェクトの個々のフィールドを変更できます。

注意:この例の目的は、hincrbyandhincrbyfloatコマンドの使用方法を示すことです。優れた設計では、使用量やコストなどの冗長な情報を保存しないようにします。