「Redis の設計と実装」を読む
1.マスタースレーブレプリケーション
In Redis, users can execute the SLAVEOF command or set the slaveof option to let a server replicate (replicate) another server. 複製されたサーバーはマスターサーバー (マスター) であり、マスターサーバーを複製するサーバーはサーバーから呼び出されます ( slave) の場合、Redis のレプリケーション機能は、同期とコマンドの伝播の 2 つの操作に分けられます。
1.1 同期
クライアントが SLAVEOF コマンドをスレーブ サーバーに送信して、スレーブ サーバーにマスター サーバーのコピーを要求する場合、スレーブ サーバーはまず同期操作を実行する必要があります。PSYNC コマンドの実行手順は次のとおりです
。
- スレーブ サーバーは PSYNC コマンドをマスター サーバーに送信します。
- PSYNCコマンドを受信したマスターサーバーは、BGSAVEコマンドを実行し、バックグラウンドでRDBファイルを生成し、バッファを使用して、これから実行されるすべての書き込みコマンドを記録します
- マスターサーバーのBGSAVEコマンドが実行されると、マスターサーバーはBGSAVEコマンドによって生成されたRDBファイルをスレーブサーバーに送信し、サーバーからRDBファイルを受信してロードし、自身のデータベースステータスをデータベースステータスに更新します。マスターサーバーが BGSAVE コマンドを実行する
- マスター サーバーはバッファに記録されたすべての書き込みコマンドをスレーブ サーバーに送信し、スレーブ サーバーはこれらの書き込みコマンドを実行して、自身のデータベース ステータスをマスター サーバー データベースの現在の状態に更新します。
PSYNC コマンドには、完全再同期と部分再同期の 2 つのモードがあります。
- 完全な再同期は、最初のレプリケーション状況を処理するために使用されます: 完全な再同期の実行手順は、基本的に SYNC コマンドの実行手順と同じです. それらはすべて、マスター サーバーによって作成され、送信されます. RDB ファイル, そしてに送信されます同期するバッファ書き込みコマンドに格納されたスレーブサーバー
- 部分的な再同期は、切断後の再レプリケーションに対処するために使用されます。切断後にスレーブサーバーがマスターサーバーに再接続するとき、条件が許せば、マスターサーバーはマスタースレーブサーバーの切断中に実行された書き込みコマンドをスレーブに送信できます。サーバーとスレーブサーバーがこれらの書き込みコマンドを受信して実行する限り、データベースはマスターサーバーの現在の状態に更新できます。
1.2 コマンドの伝播
マスターサーバーとスレーブサーバーが再び一貫した状態に戻るために、マスターサーバーはスレーブサーバーでコマンド伝播操作を実行する必要があります。マスターサーバーは、それ自体が実行した書き込みコマンド、つまり、マスター サーバーとスレーブ サーバーの不整合が発生したため、スレーブ サーバーの実行に対して、スレーブ サーバーが同じ書き込みコマンドを実行すると、マスター サーバーとスレーブ サーバーは再び整合状態に戻ります。
1.3 部分的な再同期
部分再同期機能は、次の 3 つの部分で構成されます。
- マスター サーバーのレプリケーション オフセットとスレーブ サーバーのレプリケーション オフセット。
- マスターのレプリケーション バックログ バッファー。
- サーバーの実行中の ID。
1.3.1 コピーオフセット
レプリケーションの両側のマスター サーバーとスレーブ サーバーは、レプリケーション オフセットを維持します。
- マスター サーバーが N バイトのデータをスレーブ サーバーに送信するたびに、N を自身のレプリケーション オフセットの値に追加します。
- スレーブ サーバーは、マスター サーバーから送信された N バイトのデータを受信するたびに、レプリケーション オフセットの値に N を加算します。
1.3.2 バックログバッファのコピー
これは、マスター サーバーによって保持される固定サイズの先入れ先出し (FIFO) キューで、既定のサイズは 1MB です。マスター サーバーがコマンドの伝播を実行するとき、次の図に示すように、書き込みコマンドをすべてのスレーブ サーバーに送信するだけでなく、書き込みコマンドをレプリケーション バックログ バッファーのキューに入れます。
コピー バックログ バッファは、キュー内の各バイトに対応するコピー オフセットを記録します。
スレーブ サーバーがマスター サーバーに再接続すると、スレーブ サーバーはそのレプリケーション オフセット オフセットを PSYNC コマンドを介してマスター サーバーに送信し、マスター サーバーはレプリケーション オフセットに基づいてスレーブ サーバーで実行する同期操作を決定します。
- オフセット オフセットの後のデータ (つまり、オフセット オフセット + 1 から始まるデータ) がまだレプリケーション バックログ バッファに存在する場合、マスター サーバーはスレーブ サーバーで部分的な再同期操作を実行します。
- 逆に、オフセットを超えたデータがレプリケーション バックログ バッファに存在しなくなった場合、マスターはスレーブで完全な再同期を実行します。
1.3.3 サーバー運用ID
各 Redis サーバーは、マスター サーバーまたはスレーブ サービスに関係なく、独自の実行 ID を持ちます. 実行 ID は、サーバーの起動時に自動的に生成され、40 個のランダムな 16 進数文字で構成されます.
スレーブ サーバーがマスター サーバーを初めて複製するとき、マスター サーバーはその実行中の ID をスレーブ サーバーに送信し、スレーブ サーバーは実行中の ID を保存します。
スレーブ サーバーが切断され、マスター サーバーに再接続されると、スレーブ サーバーは以前に保存された実行中の ID を現在接続されているマスター サーバーに送信します。
- スレーブ サーバーによって保存された実行中の ID が、現在接続されているマスター サーバーの実行中の ID と同じ場合、現在接続されているマスター サーバーは、スレーブ サーバーが切断される前に複製されたことを意味し、マスター サーバーは引き続き複製を試みることができます。部分的な再同期操作を実行する
- 逆に、スレーブサーバーが保存したランニングIDが現在接続中のマスターサーバーのランニングIDと一致しない場合、スレーブサーバーが切断される前にコピーされたマスターサーバーは現在接続中のマスターサーバーではなく、マスターサーバーはスレーブサーバーの完全再同期操作を実行します
1.3 心拍検出
コマンド伝播フェーズでは、スレーブ サーバーはデフォルトで 1 秒に 1 回コマンドをマスター サーバーに送信します: REPLCONF ACK <replication_offset>。ここで、replication_offset はスレーブ サーバーの現在のレプリケーション オフセットです。REPLCONF ACK コマンドの送信には、マスター サーバーとスレーブ サーバーの 3 つの機能があります。
- マスターサーバーとスレーブサーバーのネットワーク接続状態を検出
- min-slaves オプションの補助実装
- コマンドの欠落を検出
min-slaves 設定オプションの補助実装、Redis の min-slaves-to-write および min-slaves-max-lag オプションは、安全でない状況下でマスター サーバーが書き込みコマンドを実行するのを防ぐことができます。例として、マスターサーバーに次の設定を提供するとします。
min-slaves-to-write 3
min-slaves-max-lag 10
次に、スレーブ サーバーの数が 3 未満の場合、または 3 つのスレーブ サーバーすべての遅延値が 10 秒以上の場合、マスター サーバーは書き込みコマンドの実行を拒否します。
マスターサーバーからスレーブサーバーに送信された書き込みコマンドがネットワーク障害により途中で失われた場合、スレーブサーバーがマスターサーバーに REPLCONF ACK コマンドを送信すると、マスターサーバーはスレーブの現在のレプリケーションオフセットがサーバーが自身のレプリケーション オフセットよりも小さい場合、マスター サーバーは、スレーブ サーバーによって送信されたレプリケーション オフセットに従って、レプリケーション バックログ バッファー内のスレーブ サーバーから欠落しているデータを見つけ、そのデータをスレーブ サーバーに再送信します。
2.センチネル
Sentinel は Redis の高可用性ソリューションです。1 つ以上の Sentinel インスタンスで構成される Sentinel システムは、任意の数のマスター サーバーとこれらのマスター サーバーの下のすべてのスレーブ サーバーを監視でき、監視対象のマスター サーバーがスレーブを自動的にアップグレードするとオフラインになります。オフラインマスターサーバーの下のサーバーを新しいマスターサーバーに。
2.1 センチネルの起動と初期化
- サーバーを初期化する
- Sentinel 独自のコードを使用する
- センチネル状態の初期化
- Sentinel 状態の masters 属性を初期化します.
Sentinel 状態の masters ディクショナリはすべての監視対象マスター サーバー情報を記録します.キーはサーバー名であり,値は監視対象マスター サーバーに対応する sentinel.c/sentinelRedisInstance 構造です. 各 sentinelRedisInstance インスタンス構造は、マスター サーバー、スレーブ サーバー、または別のセンチネル サーバーである Redis サーバー インスタンスの監視を表します。
masters ディクショナリの初期化は、インポートされたセンチネル構成ファイルに従って実行されます。
sentinel monitor master1 127.0.0.1 6379 2
sentinel down-after-milliseconds master1 60000
sentinel failover-timeout master1 180000
sentinel parallel-syncs master1 1
sentinel monitor master2 192.168.1.3 6380 4
sentinel down-after-milliseconds master2 10000
sentinel failover-timeout master2 180000
sentinel parallel-syncs master2 5
- マスター サーバーへのネットワーク接続を作成します。Sentinel を
初期化する最後のステップは、監視対象のマスター サーバーへのネットワーク接続を作成することです。Sentinel はマスター サーバーのクライアントになります。マスター サーバーにコマンドを送信し、関連情報を取得できます。コマンド応答情報、Sentinel によって監視される各マスター サーバーについて、Sentinel はマスター サーバーへの 2 つの非同期ネットワーク接続を作成します。- コマンド接続。この接続は、メイン サーバーにコマンドを送信し、コマンド応答を受信するために特別に使用されます。
- サブスクリプション接続、この接続は、メイン サーバーの sentinel:hello チャネルをサブスクライブするために特別に使用されます
2.2 マスターサーバー情報の取得
デフォルトでは、Sentinel は10 秒ごとにコマンド接続を介して監視対象のマスター サーバーに INFO コマンドを送信し、INFO コマンドの応答を分析してマスター サーバーの現在の情報を取得します。
マスターサーバーから返された INFO コマンド応答を分析することにより、次の 2 つの側面の情報を取得できます。
- run_id フィールドに記録されたサーバー実行 ID、およびロール フィールドに記録されたサーバー ロールを含む、メイン サーバー自体に関する情報
- マスター サーバーの下にあるすべてのスレーブ サーバーに関する情報。各スレーブ サーバーは「slave」文字列で始まる行によって記録され、各行の ip= フィールドはスレーブ サーバーの IP アドレスを記録し、ポート フィールドはスレーブ サーバーを記録します。ポート番号。これらの IP アドレスとポート番号に従って、ユーザーがスレーブ サーバーのアドレス情報を提供しなくても、Sentinel は自動的にスレーブ サーバーを検出できます. Sentinel は、
3 つのスレーブ サーバーのそれぞれのインスタンス構造を作成し、これらの構造をマスター サーバー インスタンス構造に保存します。以下に示すように、スレーブディクショナリで:
2.3 サーバーから情報を取得する
マスター サーバーに新しいスレーブ サーバーがあることを Sentinel が検出すると、Sentinel は新しいスレーブ サーバーに対応するインスタンス構造を作成するだけでなく、コマンド接続とスレーブ サーバーへのサブスクリプション接続も作成します。コマンド接続を作成した後、デフォルトでは、sentinel は 10 秒ごとにコマンド接続を介して INFO コマンドをスレーブ サーバーに送信します。
INFO応答コマンドで得られる情報:
- スレーブサーバーの実行 ID run_id
- サーバーからの役割
- マスターサーバーの IP アドレス master_host、およびマスターサーバーのポート番号 master_port
- マスターサーバーとスレーブサーバーの接続ステータス master_link_status
- スレーブ優先度 slave_priority
- サーバーからのレプリケーション オフセット
この情報に基づいて、sentinel はスレーブ サーバーのインスタンス構造を更新します。
2.4 マスターサーバーとスレーブサーバーへの情報の送信
デフォルトでは、sentinel は 2 秒ごとにコマンド接続を介して、監視対象のすべてのマスター サーバーとスレーブ サーバーに次のコマンドを送信します。
PUBLISH _sentinel_:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>, <m_ip>,<m_port>,<m_epoch>"
コマンド説明:
- s_ip: Sentinel の IP アドレス
- s_port: Sentinel のポート番号
- s_runid: Sentinel の実行中の ID
- s_epoch: Sentinel の現在の構成エポック (構成エポック)
- m_port: マスターサーバーのポート番号
- m_epoch: マスターサーバーの現在の構成エポック
2.5 デジタル サーバーとセカンダリ サーバーからチャネル情報を受信する
Sentinel がマスター サーバーまたはスレーブ サーバーとのサブスクリプション接続を確立すると、Sentinel はサブスクリプション接続を介して次のコマンドをサーバーに送信します。
SUBSCRIBE _sentinel_:hello
複数の Sentinel が同じサーバーを監視している場合、1 つの Sentinel によって送信された情報は他の Sentinel によって受信され、この情報は、送信された情報の Sentinel に関する他の Sentinel の認識を更新するために使用され、他の Sentinel の認識を更新するためにも使用されます。 Sentinels: 監視対象サーバーの認識。
メイン サーバー インスタンス構造のセンチネル ディクショナリは次のとおりです。
センチネルは、受信したチャネル情報を分析することで他のセンチネルの存在を知ることができ、チャネル情報を送信することで他のセンチネルに自身の存在を知らせることができます。
Sentinel がチャネル情報を通じて新しい Sentinel を検出すると、新しい Sentinel の Sentinels ディクショナリに対応するインスタンス構造を作成するだけでなく、新しい Sentinel へのコマンド接続も作成し、新しい Sentinel もこれへの接続を作成します。 Sentinel のコマンド接続、および最後に同じマスター サーバーの複数の Sentinel が接続されたネットワークを形成します。
2.6 主観的なオフライン状態の検出
デフォルトでは、Sentinel は、コマンド接続を作成したすべてのインスタンス (マスター サーバー、スレーブ サーバー、およびその他の Sentinel を含む) に PING コマンドを 1 秒に 1 回送信し、インスタンスによって返された PING コマンド応答によって判断します。インスタンスはオンラインです。
Sentinel 構成ファイルの down-after-milliseconds オプションは、Sentinel がインスタンスがオフラインになると主観的に判断する必要がある時間を指定します。インスタンスに対応するインスタンス構造体では、構造体の flags 属性で SRI_S_DOWN フラグを有効にして、インスタンスが主観的なオフライン状態に入ったことを示します。
2.7 目的のオフライン状態を検出する
Sentinel は、メイン サーバーが主観的にオフラインであると判断した場合、メイン サーバーが本当にオフラインであるかどうかを確認するために、メイン サーバーを監視している他の Sentinel にも、メイン サーバーがログアウト状態になったと考えているかどうかを確認します (主観的ログアウトまたは客観的ログアウト)。Sentinel は、他の Sentinel から十分な数のオフライン判定 (設定で設定されたクォーラム パラメータの値) を受け取ると、プライマリ サーバーが客観的にオフラインであると判断し、プライマリ サーバーでフェイルオーバー操作を実行します。
Sentinel は次のコマンドを使用して、他の Sentinel にマスター サーバーがオフラインであることに同意するように依頼します。
SENTINEL is-master-down-byaddr <ip> <port> <current_epoch> <runid>
2.8 選挙リーダーの歩哨
マスター サーバーが客観的にオフラインであると判断されると、マスター サーバーを監視しているすべての Sentinel がリーダー Sentinel を選出します。そして、主要な Sentinel は、客観的にオフラインのマスターをフェイルオーバーします。以下は、redis がリーダー センチネルを選択するためのルールと方法です。
- すべてのオンライン Sentinel がリーダー Sentinel として選出される資格があります。つまり、同じマスター サーバーを監視している複数のオンライン Sentinel のいずれかがリーダー Sentinel になることができます。
- 各リーダー Sentinel の選出後、選出が成功したかどうかに関係なく、すべての Sentinel の構成エポックの値が 1 回インクリメントされ、構成エポックは実際にはカウンターです。
- 構成エポックでは、すべての Sentinel が特定の Sentinel をローカル リーダー Sentinel として設定する機会があり、ローカル リーダーが設定されると、この構成エポックで再度変更することはできません。
- メイン サーバーがオフラインになったことを検出した各 Sentinel は、他の Sentinel に自身をローカル リーダー Sentinel として設定するように依頼します。
- ソース Sentinel が SENTINEL is-master-down-by-addr コマンドをターゲット Sentinel に送信し、コマンドの runid が * ではなくソース Sentinel の実行 ID である場合、ソース Sentinel がターゲット Sentinel を必要とすることを意味します地元のリーダーであるセンチネルとしての地位を確立する。
- Sentinel がローカル リーダー Sentinel を設定するルールは、早い者勝ちです。
- After the target Sentinel receive the SENTINEL is-master-down-by-addr command, it will return a command reply to the source Sentinel. 応答内の Leader_runid パラメータと Leader_epoch は、ローカル リーダー Sentinel の実行中の ID と構成エポックを記録します。それぞれ対象のセンチネル。
- ソース Sentinel がターゲット Sentinel から返されたコマンドを受信した後、応答の Leader_epoch パラメータの値が自身の構成エポックと同じかどうかを確認します. 同じである場合、ソース Sentinel は、leader_runid のフェッチを続行しますLeader_runid パラメータの値がソース Sentinel と同じ場合 実行中の ID が同じ場合、ターゲット Sentinel を識別し、ソース Sentinel をローカル リーダー Sentinel として設定します。
- Sentinel が半分以上の Sentinel によってローカル リーダー Sentinel として設定されている場合、この Sentinel はリーダー Sentinel と呼ばれます。
- 指定された制限時間内にリーダー Sentinel として選出された Sentinel がいない場合、リーダー Sentinel が選出されるまでの期間の後、各 Sentinel が再度選出されます。
2.9 フォールトエスケープ
リーダー Sentinel が選出されると、リーダー Sentinel はオフライン サーバーでフェイルオーバー操作を実行します。
- オフラインマスターサーバー配下のすべてのスレーブサーバーから、スレーブサーバーを1つ選択し、マスターサーバーに変換します。
選択プロセス:- リスト内のすべてのオフラインまたは切断されたスレーブ サーバーを削除します。
- 過去 5 秒間にリーダー Sentinel の INFO コマンドを提供していないリスト内のすべてのスレーブ サーバーを削除します。
- down-after-milliseconds * 10 ミリ秒以上ダウンしたマスターから切断されたスレーブを削除します。
- 優先順位に従ってソート. 優先順位が最も高いサーバーが複数ある場合は、最大のオフセットに従ってソートします. 複数ある場合は、実行 ID に従ってソートし、最小の実行 ID を持つスレーブ サーバーを取得します.
- 代わりに、オフラインのマスター サーバーの下にあるすべてのスレーブ サーバーに新しいマスター サーバーをコピーさせます。Sentinel は、slaveof コマンドをスレーブ サーバーに送信して達成します。
- オフラインのマスター サーバーを新しいマスター サーバーのスレーブ サーバーとして設定します。古いマスター サーバーがオンラインに戻ると、新しいマスター サーバーのスレーブ サーバーになります。
3.クラスター
3.1 クラスタのデータ構造
各ノードは clusterState 構造体を保存します。これは、クラスターがオンラインかオフラインか、クラスターに含まれるノードの数、クラスターの現在の構成の時代など、現在のノードの観点からクラスターの現在の状態を記録します。オン:
typedef struct clusterState {
// 指向当前节点的指针
clusterNode *myself;
// 集群当前的配置纪元,用于实现故障转移
uint64_t currentEpoch;
// 集群当前的状态:是在线还是下线
int state;
// 集群中至少处理着一个槽的节点的数量
int size;
// 集群节点名单(包括 myself 节点)
// 字典的键为节点的名字,字典的值为节点对应的 clusterNode 结构
dict *nodes;
// ...
} clusterState;
clusterNode 構造体はノードの現在の状態を保存し、各ノードは clusterNode 構造体を使用して独自の状態を記録します。
struct clusterNode {
// 创建节点的时间
mstime_t ctime;
// 节点的名字,由 40 个十六进制字符组成
// 例如 68eef66df23420a5862208ef5b1a7005b806f2ff
char name[REDIS_CLUSTER_NAMELEN];
// 节点标识
// 使用各种不同的标识值记录节点的角色(比如主节点或者从节点),
// 以及节点目前所处的状态(比如在线或者下线)。
int flags;
// 节点当前的配置纪元,用于实现故障转移
uint64_t configEpoch;
// 节点的 IP 地址
char ip[REDIS_IP_STR_LEN];
// 节点的端口号
int port;
// 保存连接节点所需的有关信息
clusterLink *link;
// ...
};
clusterNode 構造体のリンク属性は clusterLink 構造体であり、ノードを接続するために必要な関連情報 (ソケット記述子、入力バッファー、出力バッファーなど) を格納します。
typedef struct clusterLink {
// 连接的创建时间
mstime_t ctime;
// TCP 套接字描述符
int fd;
// 输出缓冲区,保存着等待发送给其他节点的消息(message)。
sds sndbuf;
// 输入缓冲区,保存着从其他节点接收到的消息。
sds rcvbuf;
// 与这个连接相关联的节点,如果没有的话就为 NULL
struct clusterNode *node;
} clusterLink;
次の図の構造は、ノード 7000 の観点から、クラスターとクラスターに含まれる 3 つのノード 7000、7001、および 7002 の現在の状態を記録します。
3.2 CLUSTER MEET コマンドの実装
CLUSTER MEET コマンドをノード A に送信することにより、クライアントはコマンドを受信したノード A に、ノード A が現在配置されているクラスターに別のノード B を追加させることができます。
CLUSTER MEET <ip> <port>
コマンドを受信したノード A は、ノード B とハンドシェイクして互いの存在を確認し、将来のさらなる通信の基盤を築きます。
- ノード A は、ノード B の clusterNode 構造を作成し、この構造を独自の clusterState.nodes ディクショナリに追加します。
- その後、ノード A は、CLUSTER MEET コマンドで指定された IP アドレスとポート番号に従って、MEET メッセージ (メッセージ) をノード B に送信します。
- すべてがうまくいけば、ノード B はノード A から送信された MEET メッセージを受信し、ノード B はノード A の clusterNode 構造を作成し、この構造を独自の clusterState.nodes 辞書に追加します。
- その後、ノード B は PONG メッセージをノード A に返します。
- すべてがうまくいけば、ノード A はノード B から返された PONG メッセージを受信します。これにより、ノード A は、ノード B が自身が送信した MEET メッセージを正常に受信したことを知ることができます。
- その後、ノード A は PING メッセージをノード B に返します。
- すべてがうまくいけば、ノード B はノード A から返された PING メッセージを受信します。この PING メッセージを通じて、ノード B は、ノード A が自身から返された PONG メッセージを正常に受信したことを知ることができ、ハンドシェイクが完了します。
- その後、ノード A はノード B の情報を Gossip プロトコルを介してクラスタ内の他のノードに広め、他のノードもノード B と握手できるようにします。最後に、一定時間後、ノード B はすべてのノードによって認識されます。クラスター内。
3.3 スロット割り当て
Redis クラスターは断片化によってキーと値のペアをデータベースに保存します. クラスターのデータベース全体は 16384 スロット (スロット) に分割されます. データベース内の各キーは 16384 スロットのいずれかに属します. 各ノードは 0~16384 を処理できます.スロット。データベース内の 16384 スロットすべてがノードによって処理されると、クラスターはオンライン状態になります。それ以外の場合、いずれかのスロットが処理されない場合、クラスターはオフライン状態になります。
次のコマンドを実行して、スロット 0 ~ 5000 をノード 7000 に割り当てます。
127.0.0.1:7000>cluster addslots 0 1 2 3 4 ... 5000
各クラスタ ノードの状態は、現在のノードによって処理されたスロット情報を含め、cluster.h/clusterNode 構造に保存されます。
struct clusterNode {
//...
// 由这个节点负责处理的槽
// 一共有16384/8个字节长
// 每个字节的每个位记录了一个槽的保存状态
// 位的值为1表示槽正由本节点处理,值为0则表示槽并非本节点处理
// 比如slots[0]的第一个位保存了槽0的保存情况
// slots[0] 的第二个位保存了槽1的保存情况,以此类推
unsigned char slots[REDIS_CLUSTER_SLOTS/8];
// 该节点负责处理的槽数量
int numslots;
//...
}clusterNode;
このように、ノードのスロット属性を介して、現在のノードがスロットを処理するかどうかを O(1) の時間計算量で判断できます。ノードは、メッセージを介してクラスタ内の他のノードにもスロット配列を送信します
。他のノードに現在処理を担当しているスロットを通知するため。
ただし、各ノードの clusterNode.slots 配列にスロット割り当て情報のみを格納すると、スロット i が割り当てられているかどうかを知りたいときに各ノードをトラバースする必要があり、プロセス全体の責任は O(N) になります。シンプルさと効率を実現するために、Redis は各スロットの割り当て情報を cluster.h/clusterState 構造に保存します。
typedef struct clusterState {
//...
// 负责处理各个槽的节点
// 例如 slots[i] = clusterNode_A 表示槽 i 由节点 A 处理
clusterNode *slots[REDIS_CLUSTER_SLOTS];
///...
}
このように、ノードが特定のスロットを担当しているかどうかをチェックしたり、特定のスロットを特定のノードに割り当てたりするためのプログラムの時間計算量は O(1) です。
スロットを使用してデータ ストレージを割り当てる理由 クラスター内の各マスターサーバーに直接かつ均等に分散する代わりに、クラスター数の hash(key)%
1、サーバーに割り当てることができるスロットの数、スロットが少ないほど、サーバーが保存するデータが少なくなります。より多くのスロットより多く、サーバーはより多くのデータを保存し、柔軟に割り当てることができます。
2. データ移行はスロット単位で実行できます。それ以外の場合は、クラスタ全体のノード単位でデータ移行を実行する必要があります。
3.4 クラスタでコマンドを実行する
クライアントがデータベース キーに関連するコマンドをノードに送信すると、コマンドを受信したノードは、コマンドによって処理されるデータベース キーが属するスロットを計算し、スロットが自身に割り当てられているかどうかを確認します。
- それ自体に割り当てられている場合、ノードはコマンドを直接実行します。
- 自分自身に割り当てられていない場合は、移動したエラーをクライアントに返し、クライアントを正しいノードに誘導します. クライアントはエラーを受信した後、実行したいコマンドを再度送信します.
3.5 キーが属するスロットを計算する
データベース内の各キーは、16384 個のハッシュ スロットの 1 つに属しています。クラスタは、式 CRC16(key) &16384 を使用して、キーが属するスロットを計算します。CRC16(key) ステートメントは、ハッシュ スロットの CRC-16 チェックサムを計算するために使用されます。鍵のチェックとです。
3.6 リシャーディング
データベースに関するノードとスタンドアロン サーバーの違いの 1 つは、ノードはデータ番号 0 しか使用できないのに対し、スタンドアロン redis サーバーにはこの制限がないことです。
以下の図は、ノード 7000 のデータベースの状態を示しています。データベースには、リスト キー「lst」、ハッシュ キー「book」、および文字列キー「date」が含まれています。キー「lst」とキー「book」には、有効期限。
キーと値のペアをデータベースに格納するだけでなく、ノードは clusterState 構造体の slot_to_keys ジャンプ テーブルを使用して、スロットとキーの関係を保存します。
typedef struct clusterState {
// ...
zskiplist *slots_to_keys;
// ...
} clusterState;
slot_to_keys ジャンプ テーブルの各ノードのスコアはスロット番号であり、各ノードのメンバーはデータベース キーです。slots_to_keys ジャンプ
テーブルに各データベース キーが属するスロットを記録することにより、ノードは簡単にバッチを実行できます。 1 つまたはいくつかのスロットに属するすべてのデータベース キーに対する操作. たとえば、コマンド CLUSTER GETKEYSINSLOT は、スロットに属する最大数のデータベース キーを返すことができます (リシャーディングで使用すると、スロットの数 キーは新しいサーバーに移行されます)。 、およびこのコマンドは、slots_to_keys ジャンプ テーブルをトラバースすることによって実装されます。
Redis クラスターのリシャーディング操作では、ノード (ソース ノード) に割り当てられた任意の数のスロットを別のノード (ターゲット ノード) に割り当てることができ、関連するスロットが属するキーと値のペアも移動されます。ソース ノードからターゲット ノードへ。The re-sharding operation can be run online. re-sharding プロセス中、クラスターをオフラインにする必要はなく、ソース ノードとターゲット ノードの両方がコマンド リクエストの処理を続行できます。
Redis クラスターの再シャーディング操作は Redis クラスター管理ソフトウェア redis-trib によって実行され、redis-trib はソース ノードとターゲット ノードにコマンドを送信して再シャーディング操作を実行します。
3.7 ASK エラー
再シャーディング中に、ソース ノードがスロットをターゲット ノードに移行すると、移行されたスロットに属する一部のキーと値のペアがソース ノードに保存され、他のキーと値のペアがソース ノードに保存されるという状況が発生する可能性があります。ターゲットノードの内部。
クライアントがデータベース キーに関連するコマンドをソース ノードに送信し、そのコマンドによって処理されるデータベース キーが移行中のスロットに属している場合:
- ソース ノードは、まず自身のデータベースで指定されたキーを検索し、見つかった場合は、クライアントから送信されたコマンドを直接実行します。
- 逆に、ソース ノードが自身のデータベースで指定されたキーを見つけられなかった場合、ソース ノードはクライアントに ASK エラーを返し、スロットをインポートしているターゲット ノードに向きを変えるようにクライアントに指示し、そのコマンドを送信します。前に実行したかった。
ASK エラーと MOVED エラーの違い:
- MOVED エラーは、スロットの責任が 1 つのノードから別のノードに移されたことを意味します。クライアントがスロット i に関する MOVED エラーを受信した後、クライアントがスロット i に関するコマンド要求に遭遇するたびに、コマンド要求を直接ノードに送信できます。 MOVED エラーによって指されたノードに送信されます。これは、現在スロット i を担当しているノードであるためです。
- 反対に、ASK エラーは、スロットを移行するプロセスで 2 つのノードによって使用される一時的な手段にすぎません。この迂回は、スロット i に対するクライアントの将来のコマンド要求に影響を与えず、クライアントは引き続き A コマンドを送信します。 i の要求は、ASK エラーが再び発生しない限り、現在スロット i を処理しているノードに送信されます。
3.8 レプリケーションとフェイルオーバー
3.8.1 障害検出
クラスタ内の各ノードは、クラスタ内の他のノードに定期的に PING メッセージを送信して、相手がオンラインであるかどうかを検出します. PING メッセージを受信したノードが、PING メッセージを送信したノードに指定された時間内に PONG メッセージを返さない場合次に、PING メッセージを送信するノードは、PING メッセージを受信するノードをオフラインの疑いがあるとマークします。
クラスター内で、スロットの処理を担当するマスター ノードの半分以上がマスター ノード x をオフラインの疑いがあると報告した場合、マスター ノード x はオフライン (FAIL) としてマークされ、マスター ノード x は FAIL としてマークされます。オフライン ノードは、マスター ノード x に関する FAIL メッセージをクラスターにブロードキャストし、この FAIL メッセージを受信したすべてのノードは、すぐにマスター ノード x をオフラインとしてマークします。
3.8.2 フェイルオーバー
スレーブ ノードが、複製しているマスター ノードがオフライン状態になったことを検出すると、スレーブ ノードはオフラインのマスター ノードのフェールオーバーを開始します。フェールオーバーを実行する手順は次のとおりです。
- オフラインのマスター ノードを複製するすべてのスレーブ ノードの中から、1 つのスレーブ ノードが選択されます。
- 選択されたスレーブ ノードは SLAVEOF no one コマンドを実行し、新しいマスター ノードになります。
- 新しいマスター ノードは、オフラインのマスター ノードへのすべてのスロット割り当てを取り消し、これらすべてのスロットを自身に割り当てます。
- 新しいマスター ノードは、クラスターに PONG メッセージをブロードキャストします. この PONG メッセージにより、クラスター内の他のノードは、ノードがスレーブ ノードからマスター ノードに変更されたこと、およびマスター ノードがマスター ノードの責任を引き継いだことをすぐに知ることができます。オフライン ノード: 処理スロット。
- 新しいマスター ノードは、処理を担当するスロットに関連するコマンド リクエストの受信を開始し、フェイルオーバーが完了します。
3.8.3 新しいマスターノードの選出
新しいマスター ノードが選択されます。クラスターが新しいマスター ノードを選択する方法は次のとおりです。
- クラスターの構成エポックは自動インクリメント カウンターで、初期値は 0 です。
- クラスター内のノードがフェイルオーバー操作を開始すると、クラスター構成エポックの値が 1 増加します。
- 構成エポックごとに、クラスター内のスロットの処理を担当する各マスター ノードが投票する機会があり、マスター ノードからの投票を要求した最初のスレーブ ノードがマスター ノードの投票を取得します。
- スレーブ ノードは、複製しているマスター ノードがオフライン状態になったことを検出すると、メッセージをクラスターにブロードキャストし、メッセージを受信して投票権を持つすべてのマスター ノードがスレーブ ノードに投票することを要求します。
- マスター ノードに投票権があり (スロットを処理している)、このマスター ノードが他のスレーブ ノードに投票していない場合、マスター ノードは要求しているスレーブ ノードにメッセージを返し、このマスター ノードがスレーブ ノードをサポートしていることを示します。新しいマスター ノード。
- 選出に参加する各スレーブ ノードはメッセージを受信し、受信したメッセージの数に基づいて、サポートされているマスター ノードの数をカウントします。
- クラスタ内に投票権を持つマスター ノードが N 個ある場合、スレーブ ノードが N/2+1 以上の支持票を集めると、スレーブ ノードが新しいマスター ノードとして選出されます。
- 各構成エポックでは、投票権を持つ各マスター ノードは 1 回しか投票できないため、投票するマスター ノードが N 個ある場合、N/2+1 以上の支持投票を持つスレーブ ノードは 1 つしか投票できません。新しいマスター ノードは 1 つだけになります。
- 構成エポックで十分なサポート投票を収集できるスレーブ ノードがない場合、クラスターは新しい構成エポックに入り、新しいマスター ノードが選出されるまで再度選出を行います。
新しいマスター ノードを選択するこの方法は、先に紹介したリーダー Sentinel を選択する方法と非常によく似ています。これは、どちらも Raft アルゴリズムのリーダー選択方法に基づいて実装されているためです。
一般的に言えば、上記の条件を満たすスレーブ ノードはすぐに選出を開始することはありませんが、選出を試みる前にランダムな時間待機します。
DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms
一定の遅延により、FAIL ステータスがクラスター全体に伝播するのを確実に待つことができます。そうしないと、他のマスターが FAIL ステータスを認識せず、投票を拒否する可能性があります。スレーブが同時に選択を開始するのを避けるために、遅延時間はランダム化されます。
SLAVE_RANK は、このスレーブがマスターからコピーしたデータの合計量のランクを示します。マスターに障害が発生すると、スレーブはメッセージを交換して可能な限りランクを構築します. 最新のレプリケーション オフセットを持つランクは 0 で、2 番目に新しいランクは 1 であり、順番にプッシュされます. 最新のデータを保持しているスレーブが最初に (理論的には) 選択を開始することが式からわかります。ランクの低いスレーブが選出に失敗した場合、他のスレーブは後で続行されます。
3.9 メッセージ
ノードによって送信されるメッセージには、主に 5 つのタイプがあります。
- MEET メッセージ: クライアントから送信された CLUSTER MEET コマンドを送信者が受信すると、送信者は受信者に MEET メッセージを送信し、送信者が現在いるクラスターに参加するよう受信者に要求します。
- PING メッセージ: デフォルトでは、クラスター内の各ノードは既知のノード リストから毎秒 5 つのノードをランダムに選択し、5 つのノードの中で最も長い間 PING メッセージを送信していないノードに PING メッセージを送信します。選択したノードがオンラインかどうかを検出します。また、ノード B が送信した PONG メッセージをノード A が最後に受信した場合、現在時刻からの時間がノード A の cluster-node-timeout オプションの設定時間の半分を超えているため、ノード A はノードB が PING メッセージの送信対象としてランダムに選択されるため、ノード B の情報の更新に遅延が発生します。
- PONG メッセージ: 受信者は、送信者から MEET メッセージまたは PING メッセージを受信すると、MEET メッセージまたは PING メッセージが届いたことを送信者に確認するために、PONG メッセージを送信者に返します。さらに、ノードは独自の PONG メッセージをクラスターにブロードキャストして、クラスター内の他のノードがこのノードに関する知識をすぐに更新できるようにすることもできます. たとえば、フェイルオーバー操作が正常に実行された後、新しいマスター ノードは PONG をブロードキャストします.これにより、クラスタ内の他のノードは、このノードがマスター ノードになり、オフライン ノードを担当するスロットを引き継いだことがすぐにわかります。
- FAIL メッセージ: マスター ノード A が別のマスター ノード B が FAIL 状態になったと判断すると、ノード A はノード B に関する FAIL メッセージをクラスターにブロードキャストし、このメッセージを受信したすべてのノードは直ちにノード B を障害としてマークします。 .
- PUBLISH メッセージ: ノードが PUBLISH コマンドを受信すると、ノードはコマンドを実行し、PUBLISH メッセージをクラスターにブロードキャストします. PUBLISH メッセージを受信したすべてのノードは、同じ PUBLISH コマンドを実行します.