【Redis設計レビュー】`rdbcompression`の罠:CPUとI/Oの極限トレードオフを制するアーキテクチャ
テックリードの私だ。今日のコードレビュー、あるいはインフラ設計レビューで、また見かけたぞ。
`redis.conf` のデフォルト値をそのまま本番環境に投入し、「なぜか高負荷時にレイテンシが跳ね上がる」「フォロワーのレプリケーションが頻繁にタイムアウトする」と頭を抱えているエンジニアの姿を。
ターゲットは一つ、`rdbcompression` 設定だ。
「LZFアルゴリズムで文字列を圧縮するんでしょ? ディスク容量が浮くから `yes` のままでいいじゃん」――そんな浅い理解で設計しているなら、今すぐそのキーボードを置いてこの記事を読んでくれ。
今回は、Redisの永続化メカニズムの裏側にある「CPUとディスクの残酷なトレードオフ」を解剖し、実務の現場でどう判断すべきかをロジカルに叩き込む。
—
1. そもそも `rdbcompression` とは何か?
`rdbcompression` は、RDB(Redis Database)スナップショット(`BGSAVE`)をディスクに書き出す際、文字列型の値(String values)に対して LZF アルゴリズムによる圧縮を適用するかどうかを制御するフラグだ。
- `yes` (デフォルト): 圧縮する。ディスク容量を削減できるが、親プロセスからforkした子プロセスがCPUサイクルを消費する。
- `no`: 圧縮しない。CPU負荷は劇的に下がるが、RDBファイルのサイズが肥大化し、ディスクI/Oおよびネットワーク帯域を圧迫する。
一見すると「ディスクは安いんだから、CPUをケチって `no` にすればいいのでは?」あるいは「ストレージを節約したいから `yes` だ」と思いがちだが、Redisのアーキテクチャの本質は 「シングルスレッドとforkの呪縛」 にある。ここに踏み込んでいない設計はすべてモグラ叩きに終わる。
—
2. アーキテクチャの深層:裏で何が起きているか?
Redisが `BGSAVE` を実行するとき、OSの `fork()` システムコールが走り、メモリのCopy-on-Write(CoW)機構を利用して子プロセスが生成される。この子プロセスが黙々とメモリ上のデータをシリアライズし、ディスクに吐き出す。
ここで `rdbcompression yes` が有効な場合、子プロセスは以下の子細な処理を行う:
1. メモリ上のオブジェクトをスキャン。
2. それが文字列型であり、かつ一定の長さを満たしている場合、LZFアルゴリズムで圧縮を試みる。
3. 圧縮後のストリームをファイルに書き込む。
ここに潜む「2つの致命傷」
1. CPUの無駄骨(CPU Bottleneck)
LZFは高速な圧縮アルゴリズムだが、数GBから数十GBに及ぶRedisのメモリ空間全体に対してこれを実行すると、子プロセスは数コアのCPUを完全に飽和させる。もしRedisサーバーがCPUバウンドな処理(Luaスクリプトの多用など)や、他のミドルウェアと同居している場合、リソースコンテンションを引き起こす。
2. レプリケーション(Full Resync)への波及効果
RDBファイルは、単にディスクバックアップのためだけに存在するのではない。Replicationの初期同期(Full Resync) でもこのRDBファイルがそのままマスターからスレーブへ転送される。
つまり、RDBがデカければ(`rdbcompression no`)、ネットワーク帯域を食い潰し、スレーブ側のロード時間も伸びる。逆に圧縮すれば(`yes`)、マスターのCPUがさらに喘ぐことになる。
—
3. 実務での判断基準:ケーススタディ
では、我々アーキテクトはどのようにこの設定値を決定すべきか。以下のマトリクスを頭に叩き込んでおいてほしい。
パターンA:`rdbcompression yes` を維持すべきシステム
- 対象: キャッシュではなく、永続的なデータストアとしてRedisを使っている(例:数千万件のセッションデータ、JSON文字列のキャッシュ)。
- 特徴: 文字列の平均サイズが大きく(1KB〜数10KB)、かつテキストデータ(JSON, HTMLなど)が中心。LZFの圧縮率が非常に高く効く(50%以上の削減が狙える)。
- インフラ環境: CPUに余裕があり、ストレージI/O(特にクラウドのIOPS課金など)のコストをシビアに抑えたい場合。
パターンB:`rdbcompression no` にブチ伏せるべきシステム
- 対象: 超高スループット・低レイテンシが求められるキャッシュ層。データ構造の多くが Hash, ZSet など、文字列の直接圧縮の恩恵を受けにくい場合。
- 特徴: すでにバイナリデータ(画像データや暗号化されたトークンなど)を格納しており、LZFを通してもほとんど圧縮できない(エントロピーが高いデータ)。
- インフラ環境: NVMeなどの高速なローカルSSDを使用しており、ディスク容量よりもCPUのアイドル時間と `BGSAVE` の完了速度(forkの生存期間短縮)を最優先したい場合。
—
4. 検証:設定による挙動の違いを見極める
百聞は一見にしかず。手元の検証環境で、100万件のランダムな文字列(JSON風データ)を投入したRedisに対し、それぞれの挙動を計測してみよう。
設定ファイルの該当箇所
redis.conf
rdbcompression yes # または no
もしあなたがパフォーマンスチューニングを行うなら、`INFO persistence` のメトリクスを監視ツール(Prometheus + Grafanaなど)で常に注視すべきだ。
redis-cliでの確認例
127.0.0.1:6379> INFO persistence
Persistence
rdb_changes_since_last_save:1240
rdb_bgsave_in_progress:0
rdb_last_bgsave_time_sec:2 # ← BGSAVEにかかった時間(ここが重要)
rdb_last_bgsave_status:ok
`rdb_last_bgsave_time_sec` が急激に伸びている場合、メモリの肥大化とCPU圧縮処理がボトルネックになっているサインだ。
—
5. テックリードからの提言:設計チェックリスト
今後のプロジェクト設計レビューにおいては、以下の基準をクリアしているものだけマージを許可する。
1. データの性質を計測したか?
格納するString値の平均サイズとエントロピー(圧縮効率)を事前に測定したか?(ランダムなハッシュ値やバイナリに `rdbcompression yes` を使うのは、ただのCPUの無駄撃ちだ)
2. インフラのボトルネックはどこか?
CPUコア数に余裕があるならデフォルトの `yes` で安全にディスクを守れ。しかし、CPUが常に70%以上で、かつストレージ帯域に余裕があるなら、`no` への変更をベンチマークを取った上で検討しろ。
3. AOF(Append Only File)との兼ね考慮は済んでいるか?
もし `appendonly yes` を有効にしており、実質的な永続化をAOFに依存している場合、RDBの生成頻度や役割は変わってくる。システムの全体アーキテクチャから逆算して設定しているか?
—
結び
たった1行の `rdbcompression` 設定だが、ここにはRedisというプロダクトの思想――「限界までリソースを絞り出し、ミリ秒単位のレイテンシを死守する」――が凝縮されている。
「なんとなくデフォルト」を卒業し、システムの特性に合わせたリソース配分をデザインすること。それこそが、我々エンジニアがプロフェッショナルである証だ。
次回のレビューでは、根拠のある設定値と言語化されたトレードオフの解説を期待している。健闘を祈る。
コメント