【テクニカル・上級編】 rdbcompression設定 – Redis

Redisアーキテクチャの深層:`rdbcompression` が握るCPUとI/Oの極限トレードオフ

データベースエンジンの設計において、永続化(Persistence)とスループットのトレードオフは永遠の課題である。Redisはそのインメモリ構造の圧倒的な速度で知られているが、ひとたびRDBスナップショット(`BGSAVE`)が走る瞬間、メモリ上の巨大なデータセットは直列化され、ストレージへと書き出される。

このプロセスにおいて、アーキテクトが最も慎重にチューニングしなければならない設定の一つが `rdbcompression` である。

単なる「ON/OFFのスイッチ」と捉えているうちは、ジュニアクラスのエンジニアを出ていない。本稿では、LZF圧縮アルゴリズムの内部挙動、fork()後のCopy-on-Write(CoW)との相互作用、そして極限の負荷環境下におけるハードウェアリソースの奪い合いまで、Redisの深層メカニズムを解き明かす。

—

1. `rdbcompression` の正体:LZF圧縮とデータ構造のレイヤ

`rdbcompression yes`(デフォルト)は、RDBファイルへ文字列オブジェクトを書き込む際、LZF圧縮アルゴリズムを適用するかどうかを制御する。

ここで重要なのは、「すべてのデータが圧縮されるわけではない」という点だ。RedisのRDBシリアライザは、文字列の長さをチェックし、一定の閾値に満たない小さな文字列や、すでに圧縮効率が悪いバイナリデータに対してはCPUサイクルを無駄に消費することを避ける。

しかし、大規模なJSON文字列、HTMLフラグメント、あるいは連番のテキストデータなどがメモリ上に多数存在する場合、LZFは驚異的な圧縮率を発揮する。

LZFの特性とCPUバウンドの罠

LZFは非常に高速な圧縮・展開アルゴリズムであり、DEFLATE(gzip)やZstandard(zstd)と比較してCPU負荷が低いことで知られている。しかし、それは「CPU負荷がゼロ」を意味しない。

数ギガバイト、あるいは数十ギガバイトに及ぶRedisのメインメモリ空間を `BGSAVE` でダンプする際、親プロセスからフォークされた子プロセスは、自身のアドレス空間を順次スキャンし、文字列をシリアライズしていく。

[Redis 主プロセス (メモリ)]
│
├─► fork() ──► [BGSAVE 子プロセス]
│ │
│ ├─► 文字列データの取得
│ ├─► LZF圧縮処理 (ここでCPUを激しく消費)
│ └─► OS Page Cache / Disk へ逐次フラッシュ

もしデータセットが巨大であり、かつ `rdbcompression yes` が有効な場合、子プロセスは単なるI/Oバウンドではなく、強烈なCPUバウンドに陥る。

—

2. トレードオフの極限:CPU vs ディスクI/O / ネットワーク

アーキテクトが直面するジレンマは、この設定によってリソースのボトルネックがどこに移動するかにある。

| 設定値 | CPU負荷 (BGSAVE中) | RDBファイルサイズ | ディスクI/O負荷 | レプリケーション帯域 (Full Resync) |
| :— | :— | :— | :— | :— |
| `yes` (推奨) | 高 (LZFのエンコードコスト) | 最小 | 小 | 最小 (転送速度向上) |
| `no` | 極めて低 | 最大 (メモリ上のサイズにほぼ比例) | 大 | 大 (帯域を圧迫) |

「CPUを節約するために `rdbcompression no` にする」という悪手

よくある誤解として、「CPU使用率が高すぎるから `rdbcompression no` にして子プロセスの負荷を下げよう」というアプローチがある。これは大規模システムにおいては百害あって一利なしであることが多い。

1. ディスク容量の爆発: 圧縮しないRDBファイルは、メモリ上の実サイズ(あるいはそれ以上)に膨れ上がる。
2. I/Oボトルネックの悪化: ストレージへの書き込み帯域(IOPS / スループット)を圧迫し、結果として `BGSAVE` の完了時間が延びる。
3. Copy-on-Write (CoW) のメモリ枯渇リスク: `BGSAVE` が長引くということは、親プロセス側でのクライアントからの書き込みによるメモリページの変更(CoWによるページ複製)が長期間発生し続けることを意味する。結果として、メモリ使用量が二重に跳ね上がり、OOM Killerの餌食になる確率が劇的に高まる。
4. レプリケーションの遅延: マスター・レプリカ間でフルシンク(Full Resync)が発生した際、巨大な非圧縮RDBの転送はネットワーク帯域を飽和させる。

したがって、純粋なCPU枯渇対策として `rdbcompression no` を選ぶのは、システム全体の安定性をドブに捨てる行為に等しい。

—

3. 実践的アーキテクチャ判断:いつ `rdbcompression` をいじるべきか?

では、この設定をいじるべき唯一無二のシナリオとは何か? それは「CPUアーキテクチャが圧倒的に非力であり、かつストレージ帯域(あるいはネットワーク帯域)に無限の余裕がある極端なエッジ環境」、あるいは「格納データがすでに完全に圧縮済み(JPEG、Encrypted Payloadなど)である場合」だ。

すでにエントロピーが高い(ランダム性の高い)データを格納している場合、LZFはCPUサイクルをドブに捨てるだけで、ファイルサイズは1バイトも縮小しない。このようなワークロードでは、CPUの無駄な浪費を防ぐために `rdbcompression no` が合理的な選択肢となる。

観測とチューニングのためのCLIインスペクション

稼働中のRedisインスタンスでRDBの振る舞いを評価するには、単にCPU使用率を見るだけでは不十分だ。以下のコマンドを駆使し、実態を数値化せよ。

現在のメモリ使用量とデータ構造の確認
redis-cli INFO memory

直近のBGSAVEのステータス(duration_sec を注視せよ)
redis-cli INFO persistence

もし `rdb_last_bgsave_time_sec` が異常に長く、かつシステム全体のCPU使用率(特に `sys` / `user`)が `BGSAVE` 実行中に張り付いている場合、LZF圧縮がボトルネックになっているか、あるいはCoWによるメモリページフォルトが多発している兆候である。

—

4. チーフアーキテクトからの提言

Redisにおける永続化設定は、孤立したパラメータではない。メモリサイズ、スワップ設定、カーネルの `vm.overcommit_memory`、ストレージの特性(NVMeなのかNFSなのか)、そしてネットワーク帯域というエコシステム全体の中継点に位置している。

`rdbcompression` は、単に「ファイルを小さくする機能」ではない。それは「CPUという即発性のリソースを消費して、ストレージI/Oとネットワーク帯域、そして最悪の場合はメモリ空間そのものを防衛するための戦略的投資」である。

デフォルトの `yes` が大半のユースケースで正解である理由は、近代的なサーバーアーキテクチャにおいてCPUサイクルは比較的安価であり、ストレージのI/O待ちやレプリケーションのネットワーク輻輳のほうがシステム全体に与える致命傷が大きいからだ。

このトレードオフの構造を完全に理解した上で、自社のワークロード(データの性質、エントロピー、ハードウェア特性)に基づいたメトリクスを収集し、決して「なんとなく」ではなく、理論武装された根拠を持って設定を死守せよ。

コメント

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