【Redis】Redisをセッション管理・Pub/Subに使う

Redis

こんにちは、かつコーチです。

ここまでの記事で、Redisの基本操作・データ構造・キャッシュとしての使い方を扱ってきました。

今回はもう一歩踏み込んで、実務でよく採用される2つの用途、セッション管理Pub/Subを扱います。

いずれも前提知識として、String/Hash型の操作とTTLの基本を理解している前提で進めます。

セッションストアとしてのRedis

なぜセッション管理にRedisが選ばれるのか

複数台のアプリケーションサーバーで負荷分散を行う構成では、各サーバーのメモリ内にセッション情報を持たせる方式(インメモリセッション)が機能しません。

ユーザーがリクエストのたびに異なるサーバーに振り分けられると、ログイン状態を保持しているサーバーとしていないサーバーが混在してしまうためです。

この問題に対する定番の解決策が、セッションストアの外部化です。

Redisは高速な読み書きと、TTLによる自動失効の仕組みを標準で備えているため、セッションストアとして相性が良く、Laravel・Rails・Djangoなど主要フレームワークの多くが公式にRedisセッションドライバをサポートしています。

セッションデータの設計

セッションIDをキーにしたHashで、ユーザーIDや権限情報を保持するのが一般的な設計です。

# セッションIDをキーとして、ユーザー情報をHashで保存する
127.0.0.1:6379> HSET session:8f3e2a1b user_id "1001" role "admin" last_active "2026-09-02T10:00:00Z"
(integer) 3

# セッション全体にTTLを設定する(Hashのキー全体に対してEXPIREをかける)
127.0.0.1:6379> EXPIRE session:8f3e2a1b 1800
(integer) 1

# アクセスのたびにTTLを延長する(スライディングセッション)
127.0.0.1:6379> EXPIRE session:8f3e2a1b 1800
(integer) 1

ポイントは、Hashの個々のフィールドにはTTLを設定できないという制約です。

EXPIREはキー(session:8f3e2a1b)単位でしか効かないため、セッション全体を1つの単位として扱う設計にする必要があります。

ユーザーのアクセスがあるたびにEXPIREを打ち直すスライディングセッション(アクセスのたびに有効期限を延長する方式)にするか、ログイン時刻から固定時間で失効させる固定セッションにするかは、サービスの性質(金融系なら短め固定、一般的なWebサービスならスライディングが多い)に応じて選択します。

セッション管理での実務上の注意点

私が実際にRedisセッションを本番導入した際、負荷試験でセッションキーのメモリ使用量が想定より大きくなり、INFO memoryで確認したところ、user_agentlast_login_ipのような不要なフィールドまで毎回Hashに詰め込んでいたことが原因でした。

セッションストアは、SESSIONテーブルのようにあらゆる情報を持たせたくなりますが、メモリ上に載る前提のデータであることを踏まえ、本当に高速アクセスが必要な最小限のフィールドに絞るのが鉄則です。

大きな監査ログやユーザー属性は、別途RDBMS側に持たせ、セッションには参照用のIDだけを持たせる設計にすると、メモリ効率と運用の両面で健全になります。

Pub/Subによるメッセージ配信

Pub/Subの基本モデル

RedisのPub/Sub(Publish/Subscribe、発行/購読モデル)は、特定のチャンネルにメッセージを発行(Publish)すると、そのチャンネルを購読(Subscribe)している全クライアントに即座に配信される仕組みです。

チャットアプリの新着メッセージ通知や、リアルタイムダッシュボードの更新通知など、「複数のクライアントに同時に何かを知らせたい」場面で使われます。

動作確認には、redis-cliを2つのターミナルで開いて試すのが分かりやすい方法です。

# ターミナルA:チャンネル "notifications" を購読する
127.0.0.1:6379> SUBSCRIBE notifications
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "notifications"
3) (integer) 1
# ターミナルB:チャンネル "notifications" にメッセージを発行する
127.0.0.1:6379> PUBLISH notifications "新しい注文が入りました"
(integer) 1

発行と同時に、ターミナルA側には次のようなメッセージが届きます。

1) "message"
2) "notifications"
3) "新しい注文が入りました"

PUBLISHが返す(integer) 1は、そのメッセージを受信した購読者の数です。

Pub/Subの限界とストリームとの使い分け

実務で気をつけるべきなのが、RedisのPub/Subはメッセージの永続化を行わないという点です。

PUBLISHが実行された瞬間に購読していないクライアントは、そのメッセージを二度と受け取れません。

私が検証中に経験した失敗が、Pub/Subをジョブキューの代わりに使おうとしたケースです。

デプロイ作業中、ワーカープロセスを再起動した数秒間に発行されたメッセージがすべて失われ、いくつかの通知が届かないという不具合を引き起こしました。

「確実に届けたいメッセージ」「後から再送・再生したいメッセージ」を扱いたい場合は、Pub/SubではなくRedis Streams(メッセージを永続化しつつ配信できる、Kafkaに近い設計のデータ構造)や、専用のメッセージキュー(RabbitMQ・Amazon SQS等)を検討すべきです。

Pub/Subは、あくまで「今、接続しているクライアントにだけ、リアルタイムで知らせたい」用途に限定して使うのが安全な設計です。

観点Pub/SubStreams / 専用メッセージキュー
メッセージの永続化されないされる
未接続時のメッセージ消失する後から取得できる
用途リアルタイム通知(オンラインダッシュボード等)ジョブキュー、確実に処理したいイベント連携
実装の複雑さシンプルやや複雑(Consumer Group等の概念が必要)

実務での組み合わせパターン

セッション管理とPub/Subは、単独でも使われますが、組み合わせて使われる場面もあります。

例えば「特定ユーザーを強制ログアウトさせる」機能を実装する場合、対象ユーザーのセッションキーをDELすると同時に、そのユーザーが接続しているWebSocketサーバーに向けてPub/Subで通知を発行し、即座に画面をログイン画面へリダイレクトさせる、という設計です。

# 管理画面からの強制ログアウト処理(擬似的な流れ)
127.0.0.1:6379> DEL session:8f3e2a1b
(integer) 1
127.0.0.1:6379> PUBLISH force_logout:user1001 "session_revoked"
(integer) 1

このように、Redisは単一の用途だけでなく、複数の機能を組み合わせてリアルタイム性の高い体験を作れる点が上級者向けの使いどころです。

まとめ

この記事のポイント

  • セッション管理は、複数サーバー構成でのログイン状態共有という課題を解決する定番用途
  • セッションはHashで保存し、EXPIREはキー単位でのみ効く点に注意して設計する
  • Pub/SubはSUBSCRIBE/PUBLISHによるリアルタイム配信の仕組みだが、メッセージは永続化されない
  • 確実な配信が必要な用途にはPub/Subではなく、Streamsや専用メッセージキューを検討する
  • セッション失効とPub/Sub通知を組み合わせることで、強制ログアウトのようなリアルタイム制御が実現できる

次に読むべき記事

  • Redisをキャッシュとして使う:TTL戦略の基本
  • Redisのデータ構造(List・Hash・Set・Sorted Set)の使い分け
  • RedisとMemcached、どちらを選ぶべきか比較検証

タグ: Redis, 上級者向け, 実践Tips

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