【テクニカル・上級編】 Pub/Sub (パブリッシュ/サブスクライブ) – Redis

Redis Pub/Subの深淵:その「非永続性」という名の究極の武器

RedisのPub/Subを「単なるメッセージキュー」だと思っているなら、今すぐその認識を捨てろ。それはシステム設計における「妥協」ではなく、極めて意図された「制約による解放」だ。

我々がRedisのPub/Subに触れるとき、そこにあるのはACID特性への執着ではない。「極限の低レイテンシ」と「メモリ上での瞬発的なイベント伝播」である。このアーキテクチャの真髄を、内部構造の深部から紐解いていく。

—

1. 内部構造:Pub/Subは「データ構造」ではない

多くのエンジニアが勘違いしているが、RedisのPub/SubはRedisの主要なデータ型(HashやListなど)とは全く別のコードパスで動いている。

`pubsub.c` を覗けば分かる通り、これはサーバー内に保持された「グローバルなハッシュテーブル(`pubsub_channels`)」そのものだ。

  • Key: チャンネル名(文字列)
  • Value: そのチャンネルを購読しているクライアントのリスト(`list`)

この設計の何が恐ろしいか。それは、メッセージの配信処理が「計算量 O(N)」で完了することだ(Nは購読者数)。Redisのメインループをブロックすることなく、メモリ上のポインタを辿って各クライアントの出力バッファにデータを流し込む。これこそが、ディスクI/Oを一切排した「光速」の所以である。

2. なぜ「永続性」を捨てるのか?

Pub/Subの最大の特徴、それは「メッセージの永続化を行わない」ことだ。
多くの初学者はここで「メッセージが消えるリスク」を懸念する。だが、アーキテクトの視点で見れば、これは「負債の持ち込みを拒絶している」と言える。

  • RDB/AOFの介在なし: Pub/Subのメッセージは、スナップショットにもログにも書き込まれない。これが可能にするのは、メモリ帯域の限界まで叩き込めるスループットだ。
  • メモリ解放の即時性: メッセージは配信された瞬間、すべての購読者に届いたという前提でメモリから解放される。メモリの断片化を最小限に抑え、L3キャッシュヒット率を最大化する。

「メッセージが消える」のではない。「今、この瞬間の状態を伝えるためだけに最適化されている」のだ。もし永続性が必要なら、それはRedisの責務ではない。それはRedis Streamsを導入すべきドメインだ。

3. 実務における「落とし穴」と極限の最適化

Pub/Subを大規模システムで運用する際、最も注意すべきは「出力バッファの溢れ(Client Output Buffer Limits)」だ。

出力バッファの動的制御

購読者が低速なネットワークに接続していたり、処理負荷が高い場合、Redisはクライアントへ送信すべきデータをバッファに溜める。これが閾値を超えると、Redisは容赦なくコネクションを切断する。

redis.conf での推奨設定(高負荷環境)
パターン:
client-output-buffer-limit pubsub 32mb 8mb 60

この設定をチューニングせずに運用するのは、時限爆弾を抱えるのと同じだ。我々は常に「誰が遅延しているか」を監視し、`CLIENT LIST` で `obl` (output buffer length) や `omem` (output buffer memory) を追跡しなければならない。

パターンマッチングの罠

`PSUBSCRIBE` を使う際は注意が必要だ。内部的には、パターン購読者は別のリスト(`pubsub_patterns`)で管理される。パターン数が増えれば増えるほど、メッセージ配信時のマッチング処理(正規表現的な比較)がCPUを圧迫する。「パターン購読は、チャンネル名指定の購読よりも重い」という事実は、高負荷時におけるボトルネックの筆頭である。

4. アーキテクトへの問い:なぜ今もRedisなのか?

現代ではKafkaやNATSなど、強力なメッセージブローカーが数多く存在する。それでも我々がRedisのPub/Subを使い続ける理由は、「Redisエコシステム内での完結」にある。

— LuaスクリプトとPub/Subを組み合わせた「アトミックな状態更新と通知」
— データベース更新の直後にイベントを発火させる
redis.call(‘HSET’, KEYS[1], ‘status’, ‘active’)
redis.call(‘PUBLISH’, ‘channel:status’, ‘updated’)

この「状態更新」と「通知」を、同じメモリ空間で、同じネットワークホップで、かつシリアライズのオーバーヘッドなしに実行できる。これに勝るアーキテクチャを設計するのは容易ではない。

結びに代えて

RedisのPub/Subは、複雑な分散システムの中では「極めて純粋で、極めて危うい」ツールだ。
それは、「何を捨て、何を守るか」を明確に理解しているエンジニアにのみ、その真の性能を明け渡す。

メッセージの欠損を「仕様」として受け入れ、その代償としてミリ秒を削り出す。このトレードオフを理解したとき、君のシステムは初めて、スケールする準備が整うのだ。

さあ、コードに戻れ。次はどのバッファを最適化する?

コメント

タイトルとURLをコピーしました