0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Redisクラスタ内部:クライアントによるシャードの特定方法

Posted at

表紙

背景:なぜ Redis Cluster が必要なのか

Sentinel モードはマスター・スレーブ構成に基づいており、読み書きの分離を実現しています。また、自動フェイルオーバー機能も備えているため、システムの可用性が高くなります。しかし、各ノードが同じデータを保持するため、メモリの無駄が生じ、オンラインでのスケールアウトが難しいという課題があります。

そのため、Redis Cluster クラスタ(シャーディングクラスタの実装方式)が登場しました。Redis 3.0 で導入され、Redis の分散ストレージを実現しています。データをシャーディング(分割)することで、各 Redis ノードが異なるデータを保持し、オンラインスケーリングの課題を解決しています。また、データを各 Redis インスタンスに分散して保存できるため、大容量データの保存も可能で、レプリケーションやフェイルオーバー機能も提供されます。

たとえば、1 つの Redis インスタンスに 15GB 以上のデータを保存すると、応答が非常に遅くなります。これは Redis の RDB 永続化メカニズムによるもので、Redis は RDB 永続化操作を完了するために子プロセスを fork します。この fork 処理の所要時間は Redis 内のデータ量に正比例します。

このとき、「15GB のデータを分散保存すればいいのでは?」と考えるのは自然です。これこそが Redis シャーディングクラスタの目的です。シャーディングクラスタとは何か、例を見てみましょう。Redis で 15GB のデータを保存する必要がある場合、単一の Redis インスタンスを使う方法と、3 台の Redis インスタンスで構成されるシャーディングクラスタを使う方法があります。以下に比較します:

シャーディングクラスタと Redis Cluster の違い:Redis Cluster は Redis 3.0 以降で公式に提供されたシャーディングクラスタの実装方式です。

データが複数の Redis インスタンスにシャーディングされている場合、クライアントはどのインスタンスにアクセスすればよいのか、どうやって判断しているのでしょうか?以下に Redis Cluster の仕組みを見ていきましょう。

クライアントはどのようにしてアクセスすべきシャードを知るのか?

Redis Cluster は、データとインスタンス間のマッピング関係を処理するために「ハッシュスロット(Hash Slot)」という方式を採用しています。

シャーディングクラスタは、全体で 16384 個のスロット(slot)に分割されます。Redis に送信される各キー・バリューのペアは、キーに基づいてハッシュ処理され、16384 個のスロットのいずれかに割り当てられます。使用されるハッシュ関数は比較的単純で、CRC16 アルゴリズムで 16bit の値を計算し、それを 16384 で割った余り(mod)を使ってスロットを決定します。データベース内のすべてのキーはこの 16384 個のスロットのいずれかに属しており、クラスタ内の各ノードはこのスロットの一部を処理します。

クラスタ内の各ノードは、ハッシュスロットの一部分を担当しています。仮に現在のクラスタにノード A、B、C の 3 台があるとすると、各ノードが担当するハッシュスロット数は 16384 ÷ 3 になります。例として、以下のような割り当てが可能です:

  • ノード A:スロット 0 ~ 5460 を担当
  • ノード B:スロット 5461 ~ 10922 を担当
  • ノード C:スロット 10923 ~ 16383 を担当

クライアントが Redis インスタンスにデータの読み書きを要求したとき、そのインスタンスに対象のデータが存在しない場合はどうなるのでしょうか?ここで登場するのが、MOVED リダイレクトASK リダイレクトです。


3. インスタンスに対象データが存在しない場合はどうなるか?

Redis Cluster モードでは、ノードがリクエストを処理する流れは以下のようになります:

  • ハッシュスロットに基づき、現在の Redis キーが自ノードに存在するか確認
  • スロットが自ノードの担当でない場合は、MOVED リダイレクトを返す
  • スロットが自ノードの担当であり、キーがスロット内に存在する場合は、該当キーの結果を返す
  • キーがそのスロットに存在しない場合、そのスロットが「MIGRATING(移行中)」か確認
  • キーが移行中であれば、ASK リダイレクトを返してクライアントを移行先ノードに誘導
  • スロットが移行中でなければ、スロットが「IMPORTING(インポート中)」か確認
  • スロットがインポート中で ASKING フラグが立っていれば処理を継続、なければ MOVED を返す

3.1 MOVED リダイレクト

クライアントが Redis インスタンスに対して読み書き操作を行った際に、対象スロットがそのノードに属していない場合、Redis は MOVED リダイレクトエラーを返します。このエラーには、対象スロットを保持している新しいインスタンスの IP アドレスとポート番号が含まれています。これが Redis Cluster の MOVED リダイレクトメカニズムです。

3.2 ASK リダイレクト

ASK リダイレクトは主にクラスタのスケーリング(拡張・縮小)時に発生します。スケーリングによりスロットが移動するため、元のノードにアクセスすると、データはすでに移行先のノードに存在している可能性があります。この場合、ASK リダイレクトを使うことで正しいノードへ誘導できます。

4. 各ノード間の通信はどう行われるか?

Redis クラスタは複数のノードで構成されており、それぞれのノードはどのように通信しているのでしょうか?答えは Gossip プロトコルです。Gossip はメッセージ伝播プロトコルの一種で、各ノードは周期的にノードリストの中から k 個のノードを選び、自ノードが持つ情報を送信します。これを繰り返していくことで、すべてのノードの情報が一致し、アルゴリズムが収束します。

Gossip プロトコルの基本的な考え方:あるノードがネットワーク内の他のノードに情報を共有したいとき、周期的にランダムにいくつかのノードを選び、情報を送ります。情報を受け取ったノードも同様に、他のランダムなノードにその情報を送信します。通常、情報は一度に 1 ノードではなく N 個のターゲットノードに送信されます。この N のことを **fanout(ファンアウト)**と呼びます。

Redis Cluster はこの Gossip プロトコルを使って通信し、ノード同士が常に情報を交換しています。交換される情報には、ノードの障害、新ノードの参加、マスター・スレーブの変更、スロット情報などが含まれます。Gossip プロトコルでは、以下のような複数のメッセージタイプが使われます:

  • meet メッセージ:新ノードの参加を通知します。送信ノードは受信ノードに対し、現在のクラスタに参加するように通知します。meet メッセージが正常に通信されると、受信ノードはクラスタに参加し、以後は周期的に ping および pong メッセージの交換を行うようになります。
  • ping メッセージ:各ノードは毎秒、他のノードに ping メッセージを送信します。このメッセージには、ノードが把握している他の 2 つのノードのアドレス、スロット、状態情報、最終通信時間などが含まれます。
  • pong メッセージ:ping や meet メッセージを受け取ったとき、応答として pong メッセージを返します。このメッセージにも、送信ノードが把握している他の 2 つのノードの情報が含まれます。
  • fail メッセージ:あるノードが別のノードをダウンと判断した場合、その情報をクラスタ全体に fail メッセージとしてブロードキャストします。他のノードがこの fail メッセージを受信すると、該当ノードを「ダウン状態」として更新します。

特に、各ノードは クラスタバス(cluster bus) を通じて他のノードと通信します。この通信には、通常のサービス用ポートとは異なる特別なポート番号が使用されます。たとえば、あるノードのポートが 6379 の場合、クラスタ間の通信には 16379(6379 + 10000)番ポートが使われます。ノード間通信は特別なバイナリプロトコルを用いて行われます。

5. クラスタ内ノードに障害が発生した場合はどうなるか?

Redis クラスタは高可用性を実現しており、クラスタ内のノードに障害が発生しても、フェイルオーバーによってクラスタの正常なサービス提供が維持されます。

ダウン(オフライン)

Redis クラスタは ping/pong メッセージを用いて障害を検出します。この検出には「主観的ダウン」と「客観的ダウン」の 2 種類があります:

  • 主観的ダウン(Subjective Down、略称:pfail):あるノードが他のノードを「利用不可」と判断した状態です。この状態は最終的な障害判定ではなく、あくまで 1 ノードの意見であり、誤判定の可能性があります。
  • 客観的ダウン(Objective Down、略称:fail):複数のノードがあるノードを「利用不可」と認識し、クラスタ内で合意された正式なダウン状態です。もしダウンしたノードがスロットを保持するマスターノードであれば、フェイルオーバーが必要になります。
  • たとえば、ノード A がノード B を主観的ダウンと判断し、一定時間経過後にその情報を他のノード(例:ノード C)に伝えます。ノード C が受信したメッセージを解析してノード B の状態が「pfail」であると確認した場合、「客観的ダウン」のプロセスが発動されます。
  • ダウンしたのがマスターノードである場合、Redis Cluster はスロットを保持しているマスター数の過半数による投票を行い、その票数が過半数を超えたときに正式に「客観的ダウン」となります。

障害復旧(フェイルオーバー)

障害が検出された後、ダウンしたノードがマスターノードであれば、そのノードに紐づいたスレーブノードの中から 1 台を昇格させてマスターの代替とします。これによりクラスタの高可用性が保たれます。フェイルオーバーの流れは以下のとおりです:

  • 資格確認:スレーブノードがマスターの代替となる資格を持っているか確認します。
  • 選挙準備時間の設定:資格確認が通過した後、フェイルオーバー選挙のトリガー時間を更新します。
  • 選挙の開始:トリガー時間に達すると、選挙を開始します。
  • 投票の実施:スロットを保持するマスターノードだけが投票権を持ちます。スレーブノードが十分な票(過半数)を得られた場合、マスター交代が実行されます。

私たちはLeapcell、バックエンド・プロジェクトのホスティングの最適解です。

Leapcell

Leapcellは、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです:

複数言語サポート

  • Node.js、Python、Go、Rustで開発できます。

無制限のプロジェクトデプロイ

  • 使用量に応じて料金を支払い、リクエストがなければ料金は発生しません。

比類のないコスト効率

  • 使用量に応じた支払い、アイドル時間は課金されません。
  • 例: $25で6.94Mリクエスト、平均応答時間60ms。

洗練された開発者体験

  • 直感的なUIで簡単に設定できます。
  • 完全自動化されたCI/CDパイプラインとGitOps統合。
  • 実行可能なインサイトのためのリアルタイムのメトリクスとログ。

簡単なスケーラビリティと高パフォーマンス

  • 高い同時実行性を容易に処理するためのオートスケーリング。
  • ゼロ運用オーバーヘッド — 構築に集中できます。

ドキュメントで詳細を確認!

Try Leapcell

Xでフォローする:@LeapcellHQ


ブログでこの記事を読む

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?