Redisメッセージング対決 - イベント駆動型アーキテクチャのためのPub/Sub vs. Streams
James Reed
Infrastructure Engineer · Leapcell

はじめに
現代のソフトウェアアーキテクチャの活気ある景観において、イベント駆動型システムは、スケーラブルで、回復力があり、応答性の高いアプリケーションを構築するための礎として登場しました。これらのシステムの核心には効率的なメッセージングがあり、別々のコンポーネントが通信し、変化に反応することを可能にします。Redisは、その驚異的なパフォーマンスと汎用的なデータ構造で知られており、メッセージブローキングのための2つの魅力的なオプションを提供しています。Pub/SubとStreamsです。どちらも通信を容易にしますが、根本的に異なるメッセージングパラダイムに対応しています。Pub/Subは「送信して忘れる」ブロードキャスト用、Streamsは永続的で順序付けられたイベントログ用です。それらのニュアンスを理解することは、イベント駆動型設計でRedisの可能性を最大限に活用しようとするあらゆる開発者にとって不可欠です。この記事では、これらの2つのメカニズムを分析し、それらの原則、実装、および理想的なユースケースを探求し、情報に基づいたアーキテクチャの決定を下せるようにします。
Redisのメッセージングパラダイム
詳細に入る前に、議論の基盤となるコアコンセプトを明確に理解しましょう。
パブリッシャー・サブスクライバー(Pub/Sub)モデル: これは、送信者(パブリッシャー)がメッセージを特定の受信者(サブスクライバー)に直接送信しない、古典的な非同期メッセージングパターンです。代わりに、誰が聞いているかを知らずに、カテゴリまたはトピックにメッセージを発行します。サブスクライバーは、1つ以上のカテゴリに関心を示し、それらのカテゴリに発行されたすべてのメッセージを受信します。これはブロードキャストメカニズムです。
メッセージキュー/ログ: メッセージキュー、またはより一般的にはメッセージログは、サーバーレスおよびマイクロサービスアーキテクチャで使用される非同期サービス間通信の形式です。メッセージは、コンシューマーによって処理および削除されるまで、キューまたはログに永続的に保存されます。これにより信頼性が確保され、プロデューサーがオフラインであっても複数のコンシューマーがメッセージを処理できるようになります。
それでは、Redisがこれらのパラダイムをどのように実装しているのかを見てみましょう。
Redis Pub/Sub: 送信して忘れるブロードキャスト
Redis Pub/Subは、リアルタイム通知とブロードキャストのために設計された、直接的でインメモリのメッセージングシステムです。
原則: クライアントがchannelにメッセージをPUBLISHすると、Redisはそのメッセージをそのchannelに現在購読しているすべてのクライアントに即座に配信します。
主な特徴:
- 送信して忘れる: メッセージは永続化されません。メッセージが発行されたときにチャネルに購読しているクライアントがいない場合、メッセージは失われます。
- 分離: パブリッシャーとサブスクライバーは互いを完全に認識していません。
- スケーラビリティ: ネットワーク帯域幅とクライアント処理速度によってのみ制限される、多数のサブスクライバーとメッセージスループットを処理できます。
実装例:
簡単なredis-cliコマンドで例を示しましょう。
-
サブスクライバー 1:
redis-cli SUBSCRIBE chat_room_1このクライアントは現在
chat_room_1でメッセージを待っています。 -
サブスクライバー 2 (別のターミナルで):
redis-cli SUBSCRIBE chat_room_1このクライアントも同じチャネルを購読しています。
-
パブリッシャー (3番目のターミナルで):
redis-cli PUBLISH chat_room_1 "Hello everyone in chat_room_1!"サブスクライバー 1とサブスクライバー 2の両方が直ちに受信します。
1) "message" 2) "chat_room_1" 3) "Hello everyone in chat_room_1!"PUBLISHコマンドの後にクライアントが購読した場合、そのクライアントは「Hello everyone...」メッセージを受信しません。
アプリケーションシナリオ:
- リアルタイムチャットアプリケーション: オンラインユーザーにメッセージを即座に配信します。
- ライブスコアボード/フィード: アクティブユーザーに更新をプッシュします。
- キャッシュ無効化: 基盤となるデータが変更されたときに、ローカルキャッシュをリフレッシュするようにアプリケーションインスタンスに通知します。
- イベント通知: 一時的な処理のために、関心のあるサービスに即時のアラート(例:「新しい注文が入りました」)を送信します。
Redis Streams: 永続的で順序付けられたイベントログ
Redis 5.0で導入されたRedis Streamsは、追記型ログとして機能する、より堅牢で永続的なメッセージングソリューションを提供します。
原則: メッセージ(エントリ)は、一意のIDでタグ付けされた順序で、Streamの末尾に追加されます。コンシューマーはStreamの任意の時点から読み取り、読み取りの進捗を制御できます。
主な特徴:
- 永続性: メッセージは、明示的に削除またはトリミングされるまでStreamに保存されるため、永続的になります。
- 順序: エントリは、エントリIDによって順序付けられることが保証されています。
- 複数のコンシューマー/コンシューマーグループ: 複数の独立したコンシューマーが同じメッセージを処理できるようになり、コンシューマーグループがワーカー間で処理を調整できるようになり、耐障害性と負荷分散が提供されます。
- 承認された処理: コンシューマーはメッセージを承認できるため、処理済みとしてマークできます。これは信頼性の高い配信に不可欠です。
- 履歴再生: 新しいコンシューマーは、Streamの先頭からイベントの全履歴を読み取ることができます。
Streamsのコア用語:
- エントリID: Stream内の各メッセージの一意の識別子。通常はタイムスタンプとシーケンスのペア(例:
1678886400000-0)。順序を決定します。 - コンシューマー: Streamからメッセージを読み取るクライアント。
- コンシューマーグループ: Streamを処理するために協力するコンシューマーの論理グループ。コンシューマーグループに配信されたメッセージは、そのメンバー間で配布され、1人のメンバーのみが特定のメッセージを処理します。
- 保留中エントリリスト(PEL): コンシューマーグループの場合、このリストには、コンシューマーに配信されたがまだ承認されていないエントリが含まれます。コンシューマーの障害からの回復に不可欠です。
実装例:
-
Streamへのメッセージの追加:
redis-cli XADD mystream * sensor_id 123 temperature 25.5 # '*'は、Redisが一意のエントリIDを新規作成することを意味します # 応答: "1678886400000-0" (例)各
XADDコマンドは、キーと値のペアを持つエントリを追加します。 -
Streamからの読み取り(コンシューマーグループを使用):
まず、コンシューマーグループを作成します。
redis-cli XGROUP CREATE mystream mygroup 0-0 MKSTREAM # '0-0'はStreamの最初から読み取ることを意味します。 # 'MKSTREAM'は、Streamが存在しない場合に作成します。次に、
mygroupのコンシューマーが読み取ることができます。redis-cli XREADGROUP GROUP mygroup consumer1 mystream > COUNT 1 # 'consumer1'はこの特定のコンシューマーの名前です。 # '>'は、このコンシューマーグループにまだ配信されていない新しいメッセージを読み取ることを意味します。 # 'COUNT 1'は最大1メッセージを要求します。例の応答(メッセージ追加後):
1) 1) "mystream" 2) 1) 1) "1678886400000-0" 2) 1) "sensor_id" 2) "123" 3) "temperature" 4) "25.5" -
メッセージの承認: 処理後、コンシューマーはメッセージを承認します。
redis-cli XACK mystream mygroup 1678886400000-0 # 指定されたIDのメッセージを'mygroup'に対して承認します。
アプリケーションシナリオ:
- イベントソーシング: アプリケーションの状態変更の完全で順序付けられたログを保存します。
- 変更データキャプチャ(CDC): データベースの変更をイベントストリームとして記録します。
- 信頼性の高いタスクキュー: コンシューマーの障害が発生した場合でも、タスクが正確に1回処理されることを保証します。
- マイクロサービス間通信: サービス間の永続的で順序付けられたメッセージ交換。
- IoTデータ取り込み: センサーデータストリームを収集および処理します。
Pub/SubとStreamsの選択
Redis Pub/SubとStreamsの選択は、メッセージの永続性、配信保証、および消費パターンに関する具体的な要件に帰着します。
| 特徴/側面 | Redis Pub/Sub | Redis Streams |
|---|---|---|
| 永続性 | なし(送信して忘れる) | 永続的(メッセージが保存される) |
| 順序 | 複数のパブリッシャー間で保証されない | エントリIDによって保証される |
| 配信 | 少なくとも1回(サブスクライバーがいない場合、メッセージは失われる) | 少なくとも1回(承認とコンシューマーグループを使用) |
| コンシューマー追跡 | なし(サブスクライバーは匿名) | 高度(コンシューマーグループはオフセットと保留中メッセージを追跡) |
| コンシューマー回復 | 失われたメッセージを回復できない | 保留中メッセージを再読み取りできる |
| スケーラビリティ | リアルタイムブロードキャストのための高いファンアウト | 永続的イベントログのための高スループット;コンシューマーグループでスケール |
| 複雑さ | シンプルなAPI | より複雑なAPI(コンシューマーグループ、PEL、トリミング) |
| 理想的なユースケース | リアルタイム通知、一時的なイベント、チャット | イベントソーシング、CDC、信頼性の高いキュー、IoTデータ処理 |
メッセージ損失が許容され、永続性が不要なシンプルで軽量な通知の場合は、Pub/Subはそのシンプルさと高いパフォーマンスにより優れた選択肢です。耐久性、保証された配信、再生可能性、および複数のコンシューマーと耐障害性を持つ複雑な消費パターンを要求するシナリオでは、Redis Streamsが明確な勝者です。それらは、洗練されたイベント駆動型アーキテクチャを構築するための、より堅牢で機能豊富な基盤を提供します。
結論
Redis Pub/SubとStreamsはどちらもプロセス間通信のための強力なツールですが、それぞれ異なる目的を果たします。Pub/Subは、最小限のオーバーヘッドで一時的なイベントをブロードキャストすることに優れており、リアルタイムの送信して忘れるシナリオに最適です。一方、Streamsは、回復力のある、イベントソース化された、データ集約型のアプリケーションを構築するために不可欠な、永続的で順序付けられた柔軟なイベントログを提供します。それぞれの強みと弱みを理解することで、開発者はRedisを効果的に活用して、効率的でスケーラブルなイベント駆動型システムを構築できます。仕事に合ったツール(迅速なブロードキャストであれ、永続的なイベントログであれ)を選択することは、堅牢なシステム設計にとって最も重要です。

