【テクニカル・上級編】 redis-check-rdbツール – Redis

RDBの死闘:`redis-check-rdb` とバイナリレイバースエンジニアリングの極限

データベースの運用において、最も冷や汗をかく瞬間は何か。
クラスタのフェイルオーバーか?スプリットブレインか?いや、真の悪夢は「ストレージの破損によるRedisの起動拒絶」である。

メモリ内データ構造(In-Memory Data Store)であるRedisにとって、RDB(Redis Database)スナップショットファイルは、システムの心臓部である。
これがディスクI/Oの異常、カーネルパニック、あるいは不完全な `BGSAVE` によって破損したとき、Redisは起動時に容赦なくクラッシュする。

「データが消えた」

そう絶望する前に立ち上がるエンジニアが手にすべき最後の武器が、`redis-check-rdb`(Redis 5以降は `redis-server –repair` と統合、またはスタンドアロンバイナリ)である。
今回は、この隠されたユーティリティの内部挙動をバイナリレベルで解剖し、破損したRDBからデータを救い出すための極限の知見を共有する。

—

1. RDBファイルの内部構造:何がバイナリに刻まれているのか

`redis-check-rdb` がどのように動作するかを理解するには、まずRDBファイルのバイナリフォーマットの物理レイアウトを知らなければならない。
RDBは、人間が読めるテキストではない。完全にシリアライズされたバイトストリームだ。

+—————————-+—————–+
| Magic String (“REDIS”) | 5 Bytes |
+—————————-+—————–+
| RDB Version (e.g., “0009”) | 4 Bytes |
+—————————-+—————–+
| Metadata / Aux Fields | Variable |
+—————————-+—————–+
| Database Select (0xFE) | 1 Byte + Index |
+—————————-+—————–+
| Resize DB (0xFB) | Hash table sizes|
+—————————-+—————–+
| Key-Value Pairs | Type + K + V |
+—————————-+—————–+
| End of File (0xFF) | 1 Byte |
+—————————-+—————–+
| CRC64 Checksum | 8 Bytes |
+—————————-+—————–+

ファイル末尾の CRC64チェックサム が非常に重要だ。
Redisはロード時にこのチェックサムを再計算し、ファイルが途中で切り詰められていたり(Truncated)、ビット反転を起こしていないかを検証する。
もし不一致があれば、Redisは一言のエラーログを吐いて即座に終了する。

`redis-check-rdb` の本質は、このバイナリ列を先頭からパースし、各データ型のエンコーディング(Listのziplist構造、Sorted Setのskiplist構造など)の整合性をバイト単位で検証していくステートマシンに他ならない。

—

2. `redis-check-rdb` の内部メカニズムと診断フロー

破損したRDBファイルをツールに渡したとき、内部で何が起きているのか。

$ redis-check-rdb dump.rdb

このコマンドを実行すると、ツールは以下のフェーズを順に実行する。

1. マジックナンバーとバージョンの検証: 先頭5バイトの `REDIS` とバージョン文字列の整合性をチェック。
2. オプコードの逐次解析: `0xFA` (AUX)、`0xFE` (SELECTDB)、`0xFB` (RESIZEDB) などの制御オプコードをデコード。
3. ペイロードの型検証: 各キーのデータ型(文字列、リスト、ハッシュ、セット、Zセット、ストリーム)の内部エンコーディングが破損していないか検証。
4. CRC64の検証とトレイリングチェック: ファイル終端の `0xFF` と、それに続く8バイトのチェックサムを照合。

もし途中で不正なバイト列や予期せぬEOF(ファイルの終端)に遭遇した場合、ツールは「どのオフセット(Byte Offset)で破損が起きたか」を正確に指し示す。

—

3. 修復モード(Repair Mode)の限界とリスク

多くのエンジニアは「修復」という言葉に過度な期待を寄せる。
「破損したファイルを綺麗に直して、すべてのデータを復元してくれる魔法のツール」だと。

現実は非情である。

修復モード(`redis-server –repair dump.rdb` または `redis-check-rdb –fix dump.rdb`)のアルゴリズムは、実は非常にプリミティブで破壊的だ。

> 【極限の知見:修復の正体】
> 修復ツールは、バイナリを修復しているのではない。
> 「破損が検知されたオフセット以降のデータをすべて切り捨てる(Truncate)」 処理を行っているだけだ。

つまり、以下のようなトレードオフが発生する。

  • メリット: 破損箇所の手前までの「完全に無傷なデータ」を救出し、Redisを起動可能な状態に戻せる。
  • デメリット: 破損箇所以降に存在したすべてのキー(数百万件かもしれない)は永久に失われる。

修復を実行すると、ツールはファイルの末尾を検知された最後の正常なオブジェクトの境界まで巻き戻し、新たにCRC64チェックサムを再計算してファイル末尾に付与する。

—

4. 実践:バイナリレベルでの手動救出(サバイバル・エンジニアリング)

自動修復ツールが使えない、あるいは失われるデータがあまりにもクリティカルである場合、チーフアーキテクトとしての最後の手段は「バイナリの直接外科手術」である。

ケーススタディ:CRCエラーの回避とパッチ当て

もし破損が「ファイルの最後の数バイト(CRC64チェックサムの不一致)」だけで、データ自体は正常に読み込める場合、ツールの自動修復に頼る必要すらない。

1. ダミーのCRC64を書き込む、あるいはチェックサム検証を迂回する
Redisのソースコード(`src/rdb.c`)を覗いてみましよう。チェックサム検証は設定で無効化できるわけではないが、RDBファイルの末尾8バイトを削り、チェックサムなしの古いフォーマットとして強制読込させるパッチを当てる、あるいは `rdb.c` の `rdbLoad()` 関数内のCRCチェックを一時的にコメントアウトしてコンパイルし直すというアプローチが存在する。

2. ストリーミング・パーサーを用いた部分的データ抽出
もしPythonやGoでカスタムスクリプトを書くなら、サードパーティのRDBパーサーライブラリ(例: Pythonの `rdbtools`)を用いる。
`redis-check-rdb` が途中で絶命するようなファイルであっても、パース可能なセグメントまで読み込んでJSONやCSVにダンプし、新しいRedisインスタンスに `MSET` やパイプラインで流し込む方が、全体のデータロスを防げる確率が高い。

rdbtools等を用いて読み込める限りのデータを抽出する概念コード
from rdbtools import RdbParser, DebugHandler

class RescueHandler(DebugHandler):
def set(self, key, value, expire, expiry_type):
try:
# 抽出できたキーを別系統のストレージへ緊急退避
print(f”Rescued Key: {key}”)
except Exception as e:
pass

parser = RdbParser(RescueHandler())
try:
parser.parse(‘corrupted.dump’)
except Exception as e:
print(f”Hit unrecoverable corruption at offset: {e}”)

—

5. アーキテクトからの提言:RDBに依存するな

ここまで `redis-check-rdb` の極限のメカニズムと修復術を解説したが、最後にアーキテクトとして最も重要な真理を述べる。

「RDBの修復が必要になった時点で、そのインフラストラクチャ設計はすでに敗北している」

  • `BGSAVE` 中のディスク容量枯渇
  • OOM KillerによるRedisプロセスの突然死と、それに伴う半端なRDBファイルの生成
  • ストレージのハードウェア障害

これらを `redis-check-rdb` で毎回人力で直そうなどと考えてはならない。
堅牢なプロダクション環境では、以下の原則を鉄則としなければならない。

1. AOF (Append Only File) との併用: `appendfsync everysec` を基本とし、データロスを数秒以内に抑える。
2. レプリケーションの多重化: Master-Replica構成において、MasterのRDBが死んでも、Replicaがクリーンなデータセットを保持していれば即座に昇格できる(フェイルオーバーの自動化)。
3. 定期的なバックアップの整合性検証 (Chaos Engineering): バックアップが本当に復元可能か、ステージング環境で `redis-server –repair` やリストアテストを自動cronで回すこと。

ツールはあくまで最後の安全弁(Safety Net)に過ぎない。
だが、その安全弁の内部構造(バイト単位のレイアウトと切り捨てのメカニズム)を熟知しているエンジニアだけが、修羅場のインシデントにおいてシステムを死の淵から救い出すことができる。

コードを書け。バイナリを読め。そして、障害を支配しろ。

コメント

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