- スナップショットとは何か、またどこで見つけられるか
- 問題がどのように現れるか
- 考えられる復旧戦略 と、それぞれが何を意味するのか
Keeper スナップショットの概要
スナップショットとは?
スナップショットは、ある時点における Keeper の内部データ (クラスターに関するメタデータ、テーブルの協調に使用されるパス、設定など) をシリアライズした状態です。スナップショットは、クラスター内の Keeper ノードの再同期、障害発生時のメタデータ復旧、そして既知の正常な Keeper の状態に依存する起動・再起動処理において重要です。スナップショットはどこにありますか?
スナップショットは、Keeper ノードのローカルファイルシステム上にファイルとして保存されます。デフォルトでは/var/lib/clickhouse/coordination/snapshots/ に保存され、keeper_server.xml ファイルの snapshot_storage_path でカスタムパスを指定している場合は、そちらに保存されます。スナップショットには連番の名前 (例: snapshot.23) が付けられ、新しいものほど番号が大きくなります。
マルチノードのクラスターでは、各 Keeper ノードごとに独自のスナップショットディレクトリがあります。
復旧には、ノード間でスナップショットの整合性が保たれていることが極めて重要です。
破損した Keeper スナップショットの主な症状と兆候
以下の表は、破損した Keeper スナップショットでよく見られる主な症状と兆候を示しています。
ログ上の指標:
スナップショットの破損を診断する前に、まず Keeper ログ で特定のエラーパターンを確認してください。
破損した Keeper スナップショットからの復旧
ファイルに手を加える前に、必ず次の対応を行ってください:- さらなる破損を防ぐため、すべての Keeper ノードを停止する
- 協調ディレクトリ全体を安全な場所にコピーし、完全にバックアップを取る
- 少なくとも 1 つのノードに正常なデータがあることを確認するため、クラスターのクォーラムを確認する
- 既存のバックアップから復元する
次のいずれかに該当する場合は、この手順に従ってください。
- Keeper のメタデータまたはスナップショットの破損により、現在のデータを復旧できない。
- 正常な Keeper の状態を含むバックアップがある。
- 最新のバックアップを特定し、メタデータの整合性を確認します。
- ClickHouse と Keeper のサービスを停止します。
- 問題のあるスナップショットとログを、バックアップディレクトリ内のものに置き換えます。
- Keeper クラスターを再起動し、メタデータが同期されていることを確認します。
- 古いスナップショットへのロールバック
次のような場合は、この手順に従ってください。
- 最新のスナップショットは破損しているが、古いスナップショットは引き続き使用できる。
- 整合性のある復旧に必要なインクリメンタルログが無傷で残っている。
- Keeper ディレクトリから有効な古いスナップショット (例: snapshot.19) を特定して選択します。
- より新しいスナップショットとログを削除します。
- Keeper を再起動し、ログを再生してメタデータの状態を再構築させます。
SYSTEM RESTORE REPLICA を使用したメタデータの復元
次のような場合は、この手順に従ってください。
SYSTEM RESTORE REPLICA を使用したメタデータの復元- Keeper のメタデータが失われている、または破損している一方で、テーブルデータは引き続きディスク上に存在している
- ZooKeeper/Keeper のメタデータが欠落しているため、テーブルが読み取り専用モードに切り替わっている
- ローカルで利用可能なデータパーツに基づいて、Keeper 内のメタデータを再作成する必要がある
-
テーブルデータが、config の
<path>で設定されたローカルの clickHouse-server データパスに存在することを確認します (デフォルトは/var/lib/clickhouse/data/) 。 - 影響を受ける各テーブルに対して、次を実行します。
- データベースレベルで復旧する場合 (Replicated データベースエンジンを使用している場合) :
- 同期が完了するまで待ちます:
is_readonly = 0になっていることをsystem.replicasで確認し、system.detached_partsを監視して復旧を検証します
仕組み
SYSTEM RESTORE REPLICA は既存のすべてのパーツをデタッチし、Keeper 内のメタデータを再作成したうえで (新しい空のテーブルであるかのように) 、すべてのパーツを再アタッチします。これにより、ネットワーク経由でデータを再ダウンロードせずに済みます。
- Keeper 内のレプリカのメタデータを削除して再作成する
次のような場合は、この手順に従ってください。
- エラーがクラスター内の単一のレプリカで発生しており、そのレプリカの Keeper 内のメタデータが破損している、または不整合が生じている
- “Part XXXXX intersects previous part YYYYY” のようなエラーが発生する
- ローカルデータを保持したまま、レプリカの Keeper メタデータを完全にリセットする必要がある
- 影響を受けているレプリカで、テーブルをデタッチします。
- Keeper からレプリカのメタデータを削除します (任意のレプリカで実行) :
- テーブルを再アタッチします (読み取り専用モードになります) :
- レプリカのメタデータを復元します:
- 他のレプリカと同期します:
- 復旧後、すべてのレプリカで
system.detached_partsを確認します
force_restore_data フラグを使用する
サーバー起動時にすべてのレプリケートテーブルを自動復旧するには、次の手順を実行します。
- ClickHouseサーバー を停止します
- 復旧フラグを作成します:
- ClickHouseサーバーを起動します
- サーバーは自動的にフラグを削除し、すべてのレプリケートテーブルを復元します
- ログで復旧の進行状況を監視します
- Keeper クラスターを再構築する
次の場合は、この手順に従ってください。
- 復旧に使用できる有効なスナップショット、ログ、またはバックアップがない。
- Keeper クラスター全体とそのメタデータを再作成する必要がある。
- ClickHouse クラスターと Keeper クラスターを完全に停止します。
- スナップショットとログのディレクトリをクリーンアップして、各 Keeper ノードをリセットします。
- 1 つの Keeper ノードをリーダーとして初期化し、他のノードを段階的に追加します。
- 外部の記録が利用できる場合は、メタデータを再インポートします。
- 起動時にスナップショットから孤立したノードを削除する
孤立したノードとは、親ノードがスナップショット内に存在しないノードのことです。Keeper はこのような
スナップショットの読み込みを拒否し、Failure to load from latest snapshot with index というログを出力します。
この手順は、次の条件をすべて満たす場合に実施してください。
- スナップショットがまさにこの形で構造的に破損している
- 有効なバックアップや古いスナップショットが利用できない (戦略 1 および 2 を適用できる場合は、常にそちらを優先してください)
<keeper_server> の下に次の 2 つの設定をいずれも追加し、ノードを再起動します。
インメモリのノードストレージが必要ですこのクリーンアップは、インメモリのノードストレージでのみ実装されています。
<coordination_settings> で use_lsmt_storage が有効になっている場合、
remove_orphaned_nodes_on_startup を設定した状態では Keeper は起動を拒否します。復旧のために再起動する際は
use_lsmt_storage を無効にし、起動に成功した後で再度有効にしてください。WARNING
レベルで 2 行にわたってログに出力されます。1 行目には削除されたノードの総数と、削除された孤立サブツリーのルートが、
2 行目には損傷領域の境界となる、スナップショットに存在しないパスが出力されます。これらのルートより下位の子孫ノードも
削除されますが、個別には列挙されません。
Keeper はローカルのログ末尾を確認した後、Raft を開始する前に、修復済みのツリーを同じログインデックスの新しいスナップショットファイルに書き込みます。
修復が完了したスナップショットは、そのインデックスにある古いコピーよりも優先されるため、
復旧が中断されても、損傷したスナップショットが選択されることなく再開できます。新しいフォロワーには
修復済みのスナップショットが配布され、以降の再起動では
孤立ノードの削除を有効にしておく必要はありません。元のスナップショットおよび同じインデックスのコピーは、
ファイル名に orphaned_ プレフィックスが付いた状態で保持されます。これらのファイルはスナップショットの読み込みおよび自動的な
保持期間管理の対象外です。診断用に保管しておき、不要になったら手動で削除してください。
復旧のための再起動では、修復済みスナップショットと元のスナップショットのバックアップを格納するための追加のディスク容量が必要です。
修復済みスナップショットの書き込み、ローカルディレクトリの変更の同期、または元のファイルの保存に
失敗した場合、Keeper は起動を拒否します。中断されたアーカイブ処理は、次回の起動時に完了します。
スナップショットより後のローカルのログエントリが損傷領域 (削除されたノード、その配下のノード、または削除されたサブツリーの直接の親) を参照している場合、
Keeper は意図的に起動を拒否します。直接の親が競合と見なされるのは、その子ノードの集合が、ログが書き込まれた時点のツリーと一致しなくなっているためです。
そのため、親にしか触れないエントリであっても (親の配下にある兄弟ノードの create や remove など、修復済みの子ノードカウンターを更新するものも含む)、
再生結果が変わる可能性があります。一方、親の存在、バージョン、または変更 ID のみを参照するエントリは影響を受けず、
復旧を妨げません。具体的には、Check、CheckNotExists、numChildren と cversion のいずれも比較しない CheckStat、
親自身のデータまたは ACL のみを書き換える Set や SetACL、
クライアントが再接続後に再登録するデータウォッチ、子ウォッチ、存在ウォッチなどが該当します。クライアントが再登録する永続ウォッチはツリーを参照しないため、復旧を妨げることはありません。
さらに上位のノードは
影響を受けません。これらのノードは正常なピア上とまったく同じデータ、バージョン、子ノードを保持しているため、
そうしたノードにのみ触れるエントリは同一の結果で再生され、復旧を妨げません。ただし例外として、
パス配下のサブツリー全体を走査するエントリ (再帰的な削除や再帰的な一覧取得など) があります。これらは
削除されたノードを参照するため、階層の深さに関係なく競合します。ただし、Keeper がツリーを参照する前にリクエストを拒否する場合は除きます
(たとえば / の再帰的な削除)。ログ末尾にある session のクローズ
(Close) も同様で、その session が削除されたエフェメラルノードを所有していた場合、またはそのような親の直下に残っているエフェメラル
ノードを所有している場合に競合します。session をクローズするとそのエフェメラルノードが削除され、親のカウンターが更新されるためです。
このようなエントリを修復済みのツリーに対して再生すると、気付かないうちに異なる状態になってしまいます。この場合は、代わりに正常な
ピアからノードを復旧してください。ノードを停止し、その協調用ディレクトリを空にしてリーダーから再同期させるか、
戦略 1 または 2 を使用してください。