Aman Kumar Singh

Redisのリストは、両端からプッシュとポップができる順序付き文字列シーケンスです。このシンプルな構造が、2つの一般的なニーズに自然に適合します。プロデューサーが作業を追加し、コンシューマーがそれを取得するキューと、最近のアイテムの制限付きフィードです。リストはすべてのキューイング問題の答えではありません(ストリームが要求の厳しいケースを扱います)が、単純なFIFO作業や「最新N」フィードには、迅速で正しいツールです。

これはRedisマスタークラスの第5回で、ハッシュに続きます。

プッシュとポップ

リストには左右両端の操作があります:

LPUSH queue:emails "job1"    # ヘッド(左)に追加
RPUSH queue:emails "job2"    # テール(右)に追加
LPOP queue:emails            # ヘッドから削除して返す
RPOP queue:emails            # テールから削除して返す
LLEN queue:emails            # 長さ
LRANGE queue:emails 0 -1     # すべての要素

Enter fullscreen mode Exit fullscreen mode

片方の端へのプッシュと反対の端からのポップを組み合わせるとキューになります。LPUSHRPOPは先入れ先出しで、最も古いアイテムが出てきます。LPUSHLPOPは後入れ先出しでスタックになります。これらの操作はすべてアトミックなので、多くのプロデューサーとコンシューマーが同じリストに安全にアクセスできます。

シンプルな作業キュー

基本的なジョブキューは、プロデューサーがLPUSHで作業を追加し、ワーカーがRPOPで取得します:

# producer
LPUSH jobs:email '{"to":"[email protected]","template":"welcome"}'

# worker
RPOP jobs:email    # 次のジョブを取得、空ならnil

Enter fullscreen mode Exit fullscreen mode

通常のRPOPの問題は、空のキューが即座にnilを返すため、ワーカーがループでポーリングしなければならず、CPUを消費してレイテンシを増やすことです。解決策はブロッキング版です:

BRPOP jobs:email 5    # 最大5秒ジョブを待ってから返す

Enter fullscreen mode Exit fullscreen mode

BRPOPはアイテムが利用可能になるかタイムアウトするまでブロックするので、ワーカーは効率的にスリープし、ジョブが到着した瞬間に起動できます。これによりリストがポーリングなしの真のプッシュベース作業キューになります。

信頼性のギャップ

リストをジョブキューとして使用する前に知っておくべき正直な制限があります。RPOPBRPOPでは、ワーカーがジョブをポップした瞬間にリストから削除されます。そのワーカーが処理中にクラッシュした場合、ジョブはキューにも完了済みにもないため失われます。通常のリストは最大1回の配信を提供し、ドロップを許容できる作業には適していますが、許容できない作業には不適切です。

古典的な緩和策はLMOVE(または古いRPOPLPUSH)で、キューからポップして「処理中」リストにプッシュするアトミック操作を1ステップで行います:

LMOVE jobs:email jobs:processing RIGHT LEFT

Enter fullscreen mode Exit fullscreen mode

これでワーカーが処理している間、ジョブはjobs:processingにあります。成功すればワーカーはそこから削除し、クラッシュしても処理中リストに残るので回復可能です。この信頼性のあるキューパターンは損失ギャップを埋めますが、手動の簿記が必要です。真の配信保証、コンシューマーグループ、確認応答が必要な場合は、Redisストリーム(シリーズ後半)がまさにそのために設計されており、重要な作業には通常より良い選択です。リストはシンプルまたはベストエフォートなキューに使用し、信頼性の高いものにはストリームを使用してください。

制限付きフィード:「最新N」パターン

リストのもう一つの優れた用途は、最近のアイテムの境界付きリストです。最後の10件の通知、最近のアクティビティ、タイムラインなどです。新しいアイテムを先頭にLPUSHし、リストを固定長にトリミングして無制限に成長しないようにします:

LPUSH user:1:notifications "You have a new follower"
LTRIM user:1:notifications 0 9    # 最新の10件のみ保持

Enter fullscreen mode Exit fullscreen mode

LTRIMは指定された範囲を保持し、残りを破棄します。先頭にプッシュして最初のNにトリミングすることで、LRANGE user:1:notifications 0 -1で安価に読み取れる自己管理型の「最新N」リストが得られます。これは通知や最近のアイテムウィジェットなど、最新の数件のみを表示する場合にクリーンなパターンです。

リストが適切なキューである場合とそうでない場合

選択を明確に保つために:

  • リストを使用:シンプルなFIFO/LIFOキュー、失うことを許容できるベストエフォート作業、制限付き最近アイテムフィードに。最小限で高速です。
  • LMOVEで処理中リストに:完全な保証は不要だが基本的なクラッシュ回復が必要な場合に使用。
  • ストリームを使用:信頼性のある配信、確認応答、複数のコンシューマーグループ、リプレイが必要な場合に。リストはこれらのために設計されておらず、無理に使用すると脆弱なカスタム簿記につながります。

リストは単純な順序付きシーケンス構造です。両端からプッシュとポップ、作業を待つためのブロック、長さを制限するためのトリミング。シンプルなキューと最近アイテムフィードにはまさに適切で、その信頼性の限界を知ることで、いつストリームに移行すべきかがわかります。

次回はセットについて説明します。Redis内で高速なメンバーシップテストと集合演算ができる、一意な値の順序なしコレクションです。

重要なポイント

  • Redisのリストは両端からプッシュとポップできる順序付きシーケンスで、反対の端を組み合わせるとFIFOキューになります。
  • BRPOP(ブロッキングポップ)を使用すると、ワーカーが空のキューをポーリングする代わりに効率的にジョブを待つことができます。
  • 通常のリストキューは最大1回:ワーカーがポップした後にクラッシュするとジョブは失われます。
  • LMOVEで処理中リストに移動すると基本的なクラッシュ回復が追加されます。真の配信保証にはストリームを使用してください。
  • LPUSHLTRIMの組み合わせで、安価に制限付き「最新N」フィードを維持でき、通知や最近のアクティビティに最適です。

よくある質問

Redisリストでキューを構築するにはどうすればよいですか?

プロデューサーはリストにLPUSHでアイテムを追加し、ワーカーは反対の端からRPOP(またはBRPOP)でFIFO順序で取得します。ブロッキングBRPOPを使用すると、ワーカーは空のリストを繰り返しポーリングするのではなく、新しいジョブを効率的に待つことができます。

リストをジョブキューとして使用する問題は何ですか?

通常のRPOP/BRPOPはジョブを即座に削除するため、ワーカーが処理中にクラッシュするとジョブが失われます(最大1回の配信)。回復にはLMOVEで処理中リストに移動するか、真の配信保証にはRedisストリームを使用してください。

BRPOPは何をしますか?

ブロッキングポップです。リストにアイテムが利用可能になるかタイムアウトするまで待機し、その後アイテムを返します。これによりワーカーは繰り返しポーリングするのではなく、作業が到着するまでスリープでき、プッシュスタイルのキューを実現します。

リストに最新のNアイテムのみを保持するにはどうすればよいですか?

LPUSHで新しいアイテムをプッシュし、LTRIM key 0 N-1を実行して最新のNのみを保持し、残りを破棄します。これにより自己制限型の最近アイテムフィードを安価に維持できます。

キューイングにはリストとストリームのどちらを使用すべきですか?

シンプルまたはベストエフォートなキューと最近アイテムフィードにはリストを使用してください。信頼性のある配信、確認応答、コンシューマーグループ、リプレイが必要な場合はストリームを使用してください。リストは脆弱な手動簿記なしではこれらを提供しません。

参考文献


この記事は元々 amanksingh.com/blog/redis-lists-queues で公開されました。

著者について

Aman Kumar Singhはインドのノイダを拠点とするチームリード兼シニアソフトウェアエンジニアで、TypeScript、Next.js、NestJS、PostgreSQL、Redisを使用したフルスタックエンジニアリング、システム設計、本番SaaSについて執筆しています。