Redis Pub/Subの深淵:アーキテクトが語る「火と消えるメッセージ」の真実
Redisを単なる「KVS」と捉えているのであれば、それはまだ入り口に立ったに過ぎない。特にPub/Sub機能は、その「揮発性」ゆえに、並列分散処理の文脈で極めて特異な振る舞いを見せる。
多くのエンジニアが「Pub/Subはブローカーとして便利だ」と語るが、本稿ではその先へ踏み込む。RedisのPub/Subがいかにしてメモリを食いつぶし、なぜ「信頼性」という概念をあえて放棄した設計になっているのか。その内部アーキテクチャの核心を紐解こう。
—
1. 内部アーキテクチャ:計算量 O(N) の正体
RedisのPub/Subは、`dict`(ハッシュテーブル)を用いて実装されている。具体的には、チャネル名をキーとし、そのチャネルを購読しているクライアントのリストを値として保持する構造だ。
購読の瞬間(SUBSCRIBE)
クライアントが `SUBSCRIBE` を発行すると、Redisサーバー内部では、特定のクライアント接続(`client`構造体)とチャネルが結びつけられる。ここで重要なのは、購読したクライアントには「イベントループ」の監視対象として特別なマークが付与されるということだ。
メッセージの送出(PUBLISH)
`PUBLISH` が呼ばれると、Redisは対象チャネルをハッシュテーブルから即座にルックアップする。その後、そのチャネルに紐付くクライアントリストを走査し、各クライアントの出力バッファ(Output Buffer)へメッセージをコピーする。
ここがアーキテクトとしての最初の警告だ。
メッセージの配信コストは、購読者数に線形比例する。もし100万人のクライアントが同一チャネルを購読していれば、一つの `PUBLISH` は100万回のメモリコピーを引き起こす。これはRedisのメインスレッドを停止させる「爆弾」になり得る。
—
2. メモリ最適化の限界:出力バッファという名の「時限爆弾」
RedisのPub/Subにおいて、最も恐ろしいのは「低速なコンシューマーによるバッファ溢れ」だ。
クライアントの接続が低速(あるいは処理が追いついていない)場合、出力バッファには未送信のメッセージが溜まり続ける。Redisはデフォルトで `client-output-buffer-limit pubsub` を設定しているが、これを甘く見ている現場が多い。
redis.confの設定例:ここを理解せずに運用してはいけない
256MBを超えて蓄積された場合、あるいは64MBが60秒続いた場合に接続を切断する
client-output-buffer-limit pubsub 256mb 64mb 60
もしこの制限を超えると、Redisは容赦なく当該クライアントを切断する。Pub/Subは「信頼性が担保されない」設計であるがゆえに、「メッセージを追いつけない者は捨てられる」という非情なルールで成り立っている。
—
3. なぜ「Streams」ではなく「Pub/Sub」なのか?
現在、Redisには強力な `Streams` が存在する。しかし、依然としてPub/Subが生き残っている理由がある。それは「レイテンシの極限追求」だ。
- Pub/Sub: メモリ上のポインタを介した単純なブロードキャスト。書き込みは極めて高速だが、メッセージは永続化されず、オフラインのクライアントは確実にメッセージを取りこぼす。
- Streams: ログ構造のデータ構造を持ち、ACKやコンシューマーグループをサポートする。信頼性は高いが、メタデータの管理コストにより、ミリ秒単位のオーバーヘッドが発生する。
高頻度なリアルタイム・センサデータや、Web Socketのブロードキャストなど、「最新の現在値」さえ伝われば過去は不要というユースケースでは、Pub/Subこそが最適解であり続ける。
—
4. チーフアーキテクトからの助言:Pub/Sub運用の極意
現場でPub/Subを扱う際、以下の3点を徹底してほしい。
1. パターンマッチング(PSUBSCRIBE)の罠:
`PSUBSCRIBE` を多用すると、ワイルドカードマッチングのためのリスト走査が増加する。単純な `SUBSCRIBE` よりも計算量が複雑化するため、極端な高負荷環境ではチャネル設計をフラットに保つべきだ。
2. PUBSUB CHANNELSの監視:
運用フェーズでは `PUBSUB CHANNELS` コマンドで動的なチャネル状況を常にモニタリングせよ。意図しないチャネルが乱立することは、メモリリークの予兆である。
3. メッセージの「短さ」を信条とする:
Pub/Subは「信号」を送るためのものだ。巨大なペイロードをPub/Subで流すのはアーキテクチャの誤りだ。大きなデータが必要なら、Redisの別のキーに書き込み、Pub/Subで「Keyの更新通知」だけを送るべきだ。
—
結びに代えて
RedisのPub/Subは、非常に「正直」な仕組みだ。何も隠さず、何も補償せず、ただ高速にデータをばら撒く。
この「無責任さ」を理解し、アプリケーションレイヤー側で「欠損しても問題ないか」「コンシューマーが落ちても再同期できるか」という設計をやり遂げたとき、初めてあなたはRedisの真の力を引き出すことができる。
技術とは、魔法ではなく、制約の管理である。Pub/Subという制約の中で、いかにエレガントなシステムを組み上げるか。それこそが、エンジニアとしての腕の見せ所だ。
コメント