Redis Pub/Subの深淵:その「儚さ」と向き合うアーキテクチャの真実
RedisのPub/Subを単なる「メッセージキュー」だと誤解しているならば、今すぐその認識を改めるべきだ。
RedisのPub/Subは、永続化を前提としたメッセージング基盤ではない。これは「超高速なブロードキャスト・メカニズム」という名の、極めて洗練された、しかし極めて情け容赦のない「火花」だ。
今回は、我々エンジニアが本番環境で陥りがちなPub/Subの罠を、ソースコードレベルの構造から紐解いていく。
—
1. 内部構造:Pub/Subはどこで「管理」されているのか
RedisのPub/Subは、通常のKey-Valueストアのデータ構造とは全く別のメモリ空間に存在する。
Redisのソースコード(`pubsub.c`)を覗けばわかる通り、チャンネルの購読情報は `server.pubsub_channels` というハッシュテーブルに格納されている。
- Key: チャンネル名(`robj`型)
- Value: 購読しているクライアント(`client`構造体)のリスト
ここで重要なのは、Pub/SubはDBのデータセットとは完全に独立しているという点だ。`SELECT`コマンドでDB番号を切り替えても、Pub/Subの購読状態には影響を与えない。それは、Pub/SubがDBというコンテキストの外側、つまりRedisサーバーのプロセス全体に紐付いたグローバルな状態管理として実装されているからだ。
パターンマッチングの代償
`PSUBSCRIBE`を使用する場合、Redisは `server.pubsub_patterns` というリストを走査する。これはハッシュテーブルではなくリストであるため、パターン数が増大すればするほど、計算量は `O(N)` で増大する。
数千のパターンを動的に生成するような設計は、シングルスレッドであるRedisのメインループを即座にブロックする。設計段階でこの「線形探索の罠」を考慮していないアーキテクトは、大規模システムでの採用を控えるべきだ。
—
2. メッセージ消失の必然:Pub/Subは「Fire-and-Forget」の究極系
多くのジュニアエンジニアが、「なぜメッセージが届かないのか?」と頭を抱える。答えはシンプルだ。RedisのPub/Subは、購読者がいなければその瞬間にメッセージを破棄するからだ。
なぜ「永続化」されないのか
- バッファの制約: クライアントが処理しきれない速度でメッセージが届くと、Redisは出力バッファを拡大する。しかし、`client-output-buffer-limit` を超過した瞬間、Redisは迷わずそのクライアントを切断する。
- ネットワーク切断: TCPコネクションが瞬断したその瞬間に送られたメッセージは、永遠に失われる。
RedisのPub/Subには「再送」という概念が存在しない。これが「メッセージ消失のリスク」の本質である。信頼性を求めるのであれば、Redisにはストリームデータ処理のための Redis Streams (`XADD`, `XREADGROUP`) という強力な武器が別途用意されている。Pub/Subを選択するということは、「届かなくてもシステムが崩壊しない」という設計判断を明示的に行っていることに他ならない。
—
3. 実務レベルの運用:メモリとレイテンシを制御する
Pub/Subを運用する際、最も注意すべきは「メモリの爆発」と「ブロッキング」だ。
推奨される設計プラクティス
1. 接続の管理: Pub/Sub用のクライアントは、通常のKVアクセスとは完全に分離せよ。Redisはクライアントごとにバッファを持つ。Pub/Sub用のコネクションが詰まると、その影響でRedis全体のメモリ使用量がスパイクし、最悪の場合 `OOM` (Out of Memory) でRedisプロセスが終了する。
2. ペイロードの最適化: Pub/Subで巨大なJSONを流すのは悪手だ。RedisのPub/Subはスループットを最大化するために設計されている。ペイロードは最小限のIDやポインタに留め、データ本体は別の永続化ストアから引くべきだ。
// 概念的なパケット伝播のイメージ
// pubsubPublishMessage() が呼ばれると、購読者リストを巡回して
// addReplyBulk() でクライアントの出力バッファへコピーされる。
// この「コピー」が大量の購読者に対して発生することを忘れてはならない。
void pubsubPublishMessage(robj channel, robj message) {
// 1. 固定チャンネルへの配信
// 2. パターンマッチング購読者への配信
// 3. この処理中に重い処理を挟めば、Redis全体が停止する
}
—
最後に:アーキテクトへの提言
RedisのPub/Subは、その軽量さ故に、システム間の疎結合を実現する「糊」としては最高クラスのツールだ。しかし、「データの整合性」や「到達保証」が求められる場所では、絶対に使うな。
熟練したアーキテクトは、道具の限界を理解し、その限界の境界線ギリギリでシステムを設計する。Pub/Subを使うなら、それは「捨ててもいいデータ」を高速に回すためであるべきだ。
もしあなたが、Pub/Subの先に「信頼性」を求めているのであれば、それはRedisの設計思想に逆らっている。その時は迷わずRedis Streamsへ移行せよ。
Redisの美学は、シンプルさとスピードにある。その本質を理解した者だけが、この超高速なエンジンの真価を引き出すことができるのだ。
コメント