Redis監視の極意:本番障害を未然に防ぐ「3種の神器」と実務的運用パターン
テックリードの私だ。コードレビューや設計レビューで、こんなやり取りをしたことはないか?
> ジュニア開発者: 「Redisのレスポンスが最近たまにスパイクするんです。でも、何が起きているかよく分からなくて……」
> 私: 「で、`MONITOR`を本番で叩いたか?」
> ジュニア開発者: 「はい、リアルタイムに見えて便利なのでずっと流しっぱなしにしてました!」
> 私: (頭を抱えながら)「今すぐ止めろ。お前のその『便利』が、今まさに本番クラスタを沈めかけているぞ」
Redisは「超高速なインメモリDB」という美辞麗語の裏腹に、シングルスレッドで動く孤高の独裁者である。この本質を理解していないエンジニアが、安易に本番環境でデバッグツールを乱用した結果、レイテンシが跳ね上がり、コネクションプールが枯渇し、上流のアプリケーション群が雪崩式にダウンする——これは、私が幾度となく修羅場で目撃してきた光景だ。
今回は、Redisの内部アーキテクチャに踏み込み、本番環境の健康状態を正確に観測し、トラブルを未然に防ぐための「3種の神器」——`INFO`、`SLOWLOG`、そして劇薬である`MONITOR`の正しい処方箋を伝授する。
—
1. INFOコマンド:今のRedisの「脈拍」を正確に触診する
システムの異変に気づくためには、まず「正常な状態」の基準値(ベースライン)を知る必要がある。`INFO`コマンドは、Redisサーバーの統計情報やメモリ使用量、レプリケーションの状態など、数パターンのセクションに分かれた詳細なメトリクスをJSONライクなテキストで返してくれる。
実務で必ず監視すべき重要メトリクス
すべての項目を見る必要はない。本番運用の現場で、私のチェックリストに必ず入っている最重要メトリクスは以下の通りだ。
| セクション | メトリクス名 | 危険な兆候と意味 |
| :— | :— | :— |
| Memory | `used_memory_rss` vs `used_memory` | RSS(OSが割り当てた実メモリ)とRedisが認識しているメモリの乖離。メモリフラグメンテーション(断片化)が起きている証拠。 |
| Stats | `rejected_connections` | `maxclients`制限に達して拒否されたコネクション数。0以外になっていたら即座にアラートを飛ばせ。 |
| Stats | `evicted_keys` | `maxmemory`とポリシー設定により、メモリ不足で強制削除されたキーの総数。ここが増加していれば、データ設計の破綻またはメモリ不足。 |
| Stats | `instantaneous_ops_sec` | 現在の1秒あたりの処理命令数(QPS)。これが急減してい重いコマンドにブロックされている可能性。 |
| Clients | `connected_clients` | 現在接続中のクライアント数。アプリケーションのプール設定ミスで爆発することがある。 |
実務的コマンドの叩き方
すべてをダンプするとターミナルが流れるため、必要なセクションだけを絞って取得するのがプロの作法だ。
メモリ周りとCPU使用率だけをピンポイントで取得する
$ redis-cli -h 127.0.0.1 -p 6379 INFO memory
$ redis-cli -h 127.0.0.1 -p 6379 INFO stats
> architect’s note:
> これらのメトリクスは、`redis_exporter`などを経由してPrometheusにスクレイピングさせ、Grafanaで時系列ダッシュボード化するのが現代の標準だ。「目視でINFOを叩く」のは、障害発生時の初動調査以外ではあり得ない。常にダッシュボードでQPS、メモリ、ヒット率の3つを監視下に置け。
—
2. SLOWLOG:低速クエリの「現場検証」を行う
RDBにおけるスロークエリログと同様に、Redisにも実行時間が閾値を超えたコマンドを記録する仕組みがある。それが `SLOWLOG` だ。
Redisはシングルスレッドであるため、1つのコマンドが10ミリ秒(ms)かかると、その後ろに並ぶすべてのリクエストが10ミリ秒間完全にブロックされる。この「ブロッキングの元凶」を特定するための唯一の武器がこれだ。
スローログの設定(動的変更)
Redisの設定ファイル(`redis.conf`)でも定義できるが、本番稼働中に動的にチューニングすることも可能だ。
実行時間が 10000 マイクロ秒(= 10ms)を超えるコマンドを記録の対象とする
CONFIG SET slowlog-log-slower-than 10000
保持するログの最大数(FIFO方式。古いものから消える)
CONFIG SET slowlog-max-len 128
※実務上の知見として、一般的なWeb系システムであれば `10000`(10ms)を基準にし、シビアなレイテンシが求められるAPI基盤では `5000`(5ms)に設定を厳しくすることを勧める。
スローログの取得と解析
溜まったログは以下のコマンドで取得する。
直近の最新10件を取得する
$ redis-cli SLOWLOG get 10
実行結果の読み方:
1) (integer) 142 # ログの一意なID
2) (integer) 1672531200 # 実行されたUnixタイムスタンプ
3) (integer) 15420 # 実行にかかった時間(マイクロ秒。この場合は約15.4ms)
4) 1) “KEYS” # 実行されたコマンドと引数
2) “user:profile:” # ← 【戦犯】O(N)の全件走査コマンドが叩かれている!
5) “127.0.0.1:54321” # 実行したクライアントのIPとポート
6) “” # クライアント名(client setnameで設定されていれば表示)
コードレビューの視点:なぜこのスローログが出るのか?
上記の例で `KEYS “user:profile:”` がログに出ている時点で、そのコードを書いたエンジニアは即座にレビュー差し戻しだ。
Redisにおいて `KEYS` コマンドはO(N)の計算量を持ち、キー空間全体を線形探索するため、キー数が数百万件に達している本番環境では、数秒間Redisが一切の処理を受け付けなくなる「フリーズ」を引き起こす。
本番で使うべきは、カーソルベースで非ブロッキングに走査する `SCAN` コマンド、またはデータ構造自体の見直し(例:Hash構造や適切なインデックス用Sorted Setの導入)だ。
—
3. MONITOR:本番の息の根を止める「劇薬」
さて、いよいよ本題だ。新人エンジニアが真っ先に使いたがり、そしてインフラエンジニアを絶望させるコマンド `MONITOR` について話そう。
`MONITOR` は、Redisサーバーが受信したすべてのコマンドを、リアルタイムに標準出力へダンプし続けるデバッグ用コマンドである。
なぜ `MONITOR` は本番で使ってはいけないのか?
アーキテクチャの観点からその理由を数式的に理解しておこう。
1. メモリバッファの圧迫:
Redisは、`MONITOR`を有効にしたクライアントに対して、内部の出力バッファ(Output Buffer)にすべての実行コマンドを溜め込んで送信しようとする。
2. QPS(スループット)の雪崩的低下:
もしあなたのRedisが 20,000 QPS で稼働しているとする。`MONITOR`を叩いた瞬間、Redisは「毎秒2万件のテキストログをクライアントのネットワークバッファに書き出す」という凄まじいI/O負荷を背負うことになる。
3. シングルスレッドの飽和:
ネットワークI/Oとバッファリング処理にCPUリソースが奪われ、肝心のデータ処理スループットが急降下。クライアント側でタイムアウトが続発し、アプリケーション全体が連鎖的に沈没する。
> architect’s ruling:
> 「本番環境での `MONITOR` の直接実行は、本番データベースに対するDDoS攻撃と同義である」
> 原則として、ステージング環境やローカルの検証用コンテナ以外でこのコマンドを打つことは禁止せよ。もしどうしても本番のトラフィックを覗き見たい場合は、ネットワークパケットを安全にキャプチャする `redis-faina` などのオフライン解析ツールや、後述する安全な代替手段を用いるべきだ。
—
堅牢なRedis運用を実現するための設計パターン
最後に、監視ツールを駆使して問題を発見した後に、どうシステムを堅牢に設計・修正すべきかというプラクティスを提示する。
パターンA:O(N)コマンドの駆逐と `SCAN` への置き換え
前述の通り、`KEYS` や、巨大な要素数を持つ `HGETALL`、`SMEMBERS` を安易に使うな。
- アンチパターン:
# キー数が増えると数秒止まる地雷コード
all_keys = redis_client.keys(“session:”)
- 堅牢な実装:
# SCANを使ってカーソルを回し、非ブロッキングに安全に取得する
cursor = 0
match_pattern = “session:”
while True:
cursor, keys = redis_client.scan(cursor=cursor, match=match_pattern, count=100)
# 取得したキーに対する処理…
if cursor == 0:
break
パターンB:コマンドのバッチ化(パイプラインの活用)
もし `SLOWLOG` に「個々の処理は軽いのに、大量のコマンドが連続して遅延している」というログが出ている場合、それはネットワークのラウンドトリップ(RTT)のオーバーヘッドが原因だ。
`PIPELINE` を用いてコマンドを束ねて送信し、一括処理させろ。
1回ずつ往復する非効率なコード(RTTの分だけ遅延が積み重なる)
for user_id in user_ids:
redis_client.get(f”user:{user_id}”)
改善版:パイプラインで一括送信
pipe = redis_client.pipeline()
for user_id in user_ids:
pipe.get(f”user:{user_id}”)
results = pipe.execute()
—
まとめ
Redisの運用監視は、感覚で行うものではない。
1. `INFO` で日々のベースラインを把握し、メモリや断片化、コネクション枯渇の兆候をグラフで監視する。
2. `SLOWLOG` を適切に設定(例: 10ms)し、O(N)の罠にはまった不良コードを定期的に摘発・修正する。
3. `MONITOR` は「劇薬」であることを肝に銘じ、本番環境では絶対に封印する。
Redisは素直なデータベースだ。あなたが正しい作法で設計し、適切な道具で監視していれば、決して裏切ることはない。コードレビューの現場から、無知によるパフォーマンス劣化を根絶していこう。
コメント