ホームページ  >  記事  >  データベース  >  Redis の高可用性ソリューションについて話しましょう。

Redis の高可用性ソリューションについて話しましょう。

青灯夜游
青灯夜游転載
2022-01-17 10:07:032537ブラウズ

Redis の高可用性ソリューションとは何ですか?この記事では、Redis の高可用性ソリューションについて紹介します。お役に立てば幸いです。

Redis の高可用性ソリューションについて話しましょう。

Redis は通常、個別にデプロイされませんが、そうしないと単一障害点が発生することはありません。

マスター/スレーブ レプリケーション

ユーザーは、SLAVEOF コマンドまたは構成を使用して、あるサーバーを別のサーバーに複製させることができます。複製サーバーはマスターサーバーと呼ばれ、複製サーバーはスレーブサーバーと呼ばれます。このようにして、マスター サーバーにキー値を追加し、同時にスレーブ サーバーでそれを読み取ります。 [関連する推奨事項: Redis ビデオ チュートリアル ]

レプリケーション プロセスは、同期とコマンド伝播の 2 つのステップに分かれています。

同期

同期により、サーバーのデータベースのステータスがマスター サーバーの現在のデータベースのステータスに更新されます。

クライアントが SLAVEOF コマンドをスレーブ サーバーに送信すると、スレーブ サーバーは同期のために SYNC コマンド をマスター サーバーに発行します。手順は次のとおりです:

  • サーバーからマスターサーバーに対して SYNC コマンドが発行されます。

  • SYNC コマンドを受信したメイン サーバーは、BGSAVE コマンドを実行し、バックグラウンドで RDB ファイルを生成し、バッファを使用して今後実行されるすべての書き込みコマンドを記録します。

  • マスターサーバーのBGSAVEコマンド実行後、マスターサーバーはBGSAVEで生成されたRDBファイルをスレーブサーバーに送信し、スレーブサーバーはRDBファイルを受信して​​ロードし、データベースの状態を取得します。マスター サーバーが BGSAVE コマンドを実行すると、サーバーからのデータベース ステータスが更新されます。

  • マスター サーバーはバッファ内のすべての書き込みコマンドをスレーブ サーバーに送信し、スレーブ サーバーはこれらのコマンドを実行します。コマンドを書き込み、データベース ステータスをマスターに更新します。 サーバーの現在のデータベース ステータス。

Redis の高可用性ソリューションについて話しましょう。

コマンドの伝播

同期操作が完了すると、マスター サーバーとスレーブ サーバーのデータベースの状態は一致していますが、マスター サーバーがクライアントの書き込みコマンドを受信した後、マスター データベースとスレーブ データベースの間でデータの不一致が発生します。このとき、コマンドの伝播によってデータベースは一致します。

PSYNC 同期の最適化

2.8 より前の同期は毎回完全同期です。サーバー しばらく切断されました。実際には最初から同期する必要はなく、切断されている間だけデータを同期する必要があります。そのため、バージョン 2.8 では、SYNC コマンドの代わりに PSYNC が使用されるようになりました。

PSYNC は、完全同期と部分同期の 2 つの状況に分かれており、完全同期は初期同期状態を処理し、部分同期は切断と再接続の状況を処理します。

部分同期の実装

部分同期は主に次の 3 つの部分を使用します。

    マスター サーバーとスレーブのレプリケーション オフセットサーバー レプリケーション オフセット
  • マスター サーバーのレプリケーション バックログ バッファ
  • サーバーの実行 ID

レプリケーション オフセット

マスター サーバーのレプリケーション オフセット : マスター サーバーが N バイトのデータをスレーブ サーバーに送信するたびに、マスター サーバーは自身のレプリケーション オフセットをスレーブ サーバーのレプリケーション オフセットに変更します。 シフト : スレーブ サーバーが N バイトを受信するたびに、マスターサーバーによって伝播されたデータのバイト数に応じて、マスターサーバー自体のレプリケーションオフセットが N だけ変更されます マスター サーバーとスレーブ サーバーが一貫した状態にある場合、それらのオフセットは常に同じになりますが、オフセットが等しくない場合、それらは不整合な状態になります。

コピー バックログ バッファ

コピー バックログ バッファは、メイン サーバーによって維持される固定長の FIFO キューです。デフォルトのサイズは 1MB です。最大長に達すると、最初のキューが入力され、キュー内の要素は新しく結合された要素のために場所を空けるために排出されます。

redis コマンドが伝播されると、スレーブ サーバーだけでなく、レプリケーション バックログ バッファーにも送信されます。


Redis の高可用性ソリューションについて話しましょう。スレーブ サーバーがマスター サーバーに再接続すると、スレーブ サーバーは PSYNC コマンドを通じて独自のレプリケーション オフセット offset をマスター サーバーに送信し、マスター サーバーはレプリケーション オフセット量に基づいて、部分同期を使用するか完全同期を使用するかを決定します。 オフセット offset 後のデータがまだバックログ バッファにコピー中の場合は、部分同期が使用され、それ以外の場合は完全同期が使用されます。

(本には判断方法が書いてありません。マスター コピーのオフセットからスレーブ コピーのオフセットを引いた値になると思います。1MB を超える場合は、バッファ バックログにないデータがあるということですか?)


サーバー実行 ID

#サーバーが起動すると、サーバー実行 ID として 40 桁のランダムな文字が生成されます。

スレーブ サーバーが初めてマスター サーバーに複製するとき、マスター サーバーはその実行 ID をスレーブ サーバーに送信し、スレーブ サーバーはこの実行 ID を保存します。スレーブ サーバーが切断され、再接続されると、保存された実行 ID がスレーブ サーバーに送信されます。スレーブ サーバーによって保存された実行 ID が現在のマスター サーバーの実行 ID と同じである場合、部分的な同期が試行されます。異なる場合は、完全同期が実行されます。

#PSYNC の全体的なプロセス

Redis の高可用性ソリューションについて話しましょう。

##ハートビート検出コマンド伝播フェーズでは、スレーブ サーバーはデフォルトで 1 秒に 1 回の頻度でマスター サーバーにコマンドを送信します。

REPLICONF ACK
ここで、replication_offset は現在のスレーブサーバーのレプリケーションオフセット、シフト量。 REPLICONF ACK コマンドの送信には、マスター サーバーとスレーブ サーバーの 3 つの機能があります。

    マスター サーバーとスレーブ サーバーのネットワーク接続ステータスを検出します。
  • min-slaves オプションのアシスタント実装。
  • #検出コマンドが失われました。
  • #マスター サーバーとスレーブ サーバーのネットワーク接続ステータスを検出する

マスター サーバーとスレーブ サーバーは、次の方法で 2 つのサーバー間のネットワーク接続ステータスを確認できます。 REPLICONF ACK コマンドの送受信 ネットワーク接続が正常かどうか: マスター サーバーがスレーブ サーバーから REPLICONF ACK コマンドを 1 秒以上受信しない場合、マスター サーバーはマスターとサーバーの間に問題があることを認識します。奴隷。

min-slaves オプションの補助実装

redis の min-slaves-to-write および

min-slaves-max-lag

2 つのオプションにより、安全でない状況でマスター/スレーブ サーバーが書き込みコマンドを実行するのを防ぐことができます。 <pre class="brush:js;toolbar:false;">min-slaves-to-write 3 min-slaves-max-lag 10</pre>上記のように設定した場合、スレーブ サーバーの数が 3 台未満の場合、または 3 台のスレーブ サーバーすべての遅延が 10 秒以上の場合、マスター サーバーは接続を拒否することを意味します。書き込みコマンドを実行します。

コマンド損失の検出

マスター サーバーからスレーブ サーバーに伝播された書き込みコマンドがネットワーク障害により途中で失われた場合、スレーブ サーバーがREPLICONF ACK コマンドをマスター サーバーに送信すると、マスター サーバーはスレーブ サーバーの現在のレプリケーション オフセットが自身のオフセットよりも小さいことを検出し、マスター サーバーはレプリケーションに基づいてレプリケーション バッファー内のスレーブ サーバーから欠落しているデータを見つけることができます。スレーブ サーバーのオフセットを設定し、再書き込みがスレーブ サーバーに送信されます。

マスター スレーブ レプリケーションの概要

実際、マスター スレーブ レプリケーションでは、RDB と AOF が存在する場合でも、データの追加コピーがバックアップされます。サーバー上のマシン全体がハングアップした場合、マスター/スレーブ レプリケーションにより、マスター サーバーとスレーブ サーバーを 2 つの異なるマシンにデプロイできます。この方法では、マスター サーバー マシンがハングアップした場合でも、手動でスレーブサーバーに切り替えてサービスを継続してください。

センチネル

マスター/スレーブはデータのバックアップを実装していますが、マスター サーバーがハングアップした場合は、スレーブ サーバーから手動で切り替える必要があります。マスターサーバーに。 Sentinel は、プライマリ サーバーがハングアップしたときに、セカンダリ サーバーからプライマリ サーバーに自動的に切り替えることができます。

センチネル システムはすべてのマスター サーバーとスレーブ サーバーを監視できます。サーバー 1 が現在オフラインであると仮定します。サーバー 1 のオフライン時間がユーザーによって設定されたオフライン時間の上限を超えると、センチネル システムはサーバー 1 のフェイルオーバーを実行します。

Redis の高可用性ソリューションについて話しましょう。

まず、センチネル システムはスレーブの 1 つを選択します。 server1.server の下に移動し、選択したスレーブ サーバーを新しいマスター サーバーにアップグレードします。

  • その後、センチネル システムは、server1 の下のすべてのスレーブ サーバーに新しいレプリケーション コマンドを送信し、それらが新しいマスター サーバーのスレーブ サーバーになることを許可します。すべてのスレーブ サーバーが新しいマスター サーバーに複製されると、フェイルオーバー操作が完了します。

  • さらに、sentinel はオフラインのサーバー 1 も監視し、オンラインに戻ると、新しいマスター サーバーのスレーブ サーバーとして設定されます。

  • #センチネル状態の初期化

struct sentinelState {
    char myid[CONFIG_RUN_ID_SIZE+1]; 
    // 当前纪元,用于实现故障转移
    uint64_t current_epoch;
    // 保存了所有被这个sentinel监视的主服务器
    // 字典的键是主服务器的名字
    // 字典的值是指向sentinelRedisInstance结构的指针
    dict *masters;
    // 是否进入了TILT模式
    int tilt;         
    // 目前正在执行的脚本数量
    int running_scripts;   
    // 进入TILT模式的时间
    mstime_t tilt_start_time;   
    // 最后一次执行时间处理器的时间
    mstime_t previous_time;     
    // 一个fifo队列,包含了所有需要执行的用户脚本
    list *scripts_queue;            
    char *announce_ip;  
    int announce_port; 
    unsigned long simfailure_flags; 
    int deny_scripts_reconfig;
    char *sentinel_auth_pass;   
    char *sentinel_auth_user;    
    int resolve_hostnames;      
    int announce_hostnames;     
} sentinel;
#センチネル状態のマスター属性の初期化

masters は、sentinel によって監視されているすべてのマスター サーバーに関する情報を記録します。ディクショナリのキーは監視対象サーバーの名前で、値は監視対象サーバーに対応する SentinelRedisInstance 構造体です。 SentinelRedisInstance は、マスター サーバー、スレーブ サーバー、またはその他のセンチネル インスタンスであるセンチネル サーバーによって監視されるインスタンスです。

typedef struct sentinelRedisInstance {
    // 标识值,记录实例的类型,以及该实例的当前状态
    int flags;  
    // 实例的名字
    // 主服务器名字在配置文件中设置
    // 从服务器和sentinel名字由sentinel自动设置,格式是ip:port
    char *name; 
    // 运行id
    char *runid;   
    // 配置纪元,用于实现故障转移
    uint64_t config_epoch;  
    // 实例的地址
    sentinelAddr *addr; /* Master host. */
    // 实例无响应多少毫秒之后,判断为主观下线
    mstime_t down_after_period; 
    // 判断这个实例为客观下线所需的支持投票数量
    unsigned int quorum;
    // 执行故障转移,可以同时对新的主服务器进行同步的从服务器数量
    int parallel_syncs; 
    // 刷新故障迁移状态的最大时限
    mstime_t failover_timeout;  
    // 除了自己外,其他监视主服务器的sentinel
    // 键是sentinel的名字,格式是ip:port
    // 值是键对应的sentinel的实例结构
    dict *sentinels;  
    // ...
} sentinelRedisInstance;
メイン サーバーへのネットワーク接続を作成する

センチネルを初期化する最後の手順は、監視対象のメイン サーバーへのネットワーク接続を作成することです。メインサーバーへの接続が 2 つ作成されます。

コマンド接続: コマンドをメインサーバーに具体的に送信し、コマンド応答を受信します。 Redis の高可用性ソリューションについて話しましょう。サブスクリプション接続

: メイン サーバーの _sentinel_:hello チャネルにサブスクライブするために特別に使用されます。

メイン サーバー情報の取得

Sentinel は、デフォルトでコマンド接続を通じて監視対象のメイン サーバーに INFO コマンドを 10 秒ごとに送信し、メイン サーバーの現在の情報を取得します。返事。返信して次の情報を取得します。

  • マスターサーバーの run_id
  • マスターサーバー配下のすべてのスレーブサーバーの情報。

sentinelRedisInstance の名前辞書と runid フィールドは、この情報に基づいて更新できます。

スレーブ サーバー情報の取得

sentinel は、スレーブ サーバーへのコマンド接続とサブスクリプション接続も作成します。

Redis の高可用性ソリューションについて話しましょう。

デフォルトでは、sentinel はコマンド接続を通じて 10 秒ごとにスレーブ サーバーに INFO コマンドを送信し、応答を通じてスレーブ サーバーから現在の情報を取得します。応答は次のとおりです。

Redis の高可用性ソリューションについて話しましょう。

    #スレーブ サーバーの実行 ID
  • スレーブ サーバーの役割
  • マスターサーバーのIPとポート
  • ##マスターサーバーの接続ステータスmaster_link_status
  • ##スレーブサーバーの優先度slave_priority
  • ##スレーブのレプリケーションオフセット変数サーバー
  • 情報によると、応答情報に基づいて、センチネルはスレーブ サーバーのインスタンス構造を更新できます。
マスター サーバーとスレーブ サーバーのサブスクリプション接続に情報を送信する

デフォルトでは、sentinel は監視対象のマスター サーバーとスレーブ サーバーに 2 秒ごとに情報を送信します。注文。

s_ipRedis の高可用性ソリューションについて話しましょう。:sentinel の IP アドレス

s_port:sentinel のポート番号
s_runid :sentinel が実行中の id
s_epoch:sentinel の現在の設定エポック
m_name:プライマリ サーバーの名前
m_ip:primary サーバーのip アドレス
m_port: メイン サーバーのポート番号
m_epoch: メイン サーバーの現在の構成エポック 情報を Sentinel_:hello チャネルに送信します、同じサーバーを監視している他の監視員 (あなた自身を含む) によっても監視されます。
他のセンチネルへのコマンド接続を作成する

センチネルは相互にコマンド接続を作成します。同じコマンドを監視する複数のセンチネルは、相互接続されたネットワークを形成します。

#センチネル間にはサブスクリプション接続は作成されません。 Redis の高可用性ソリューションについて話しましょう。

主観的なオフライン ステータスの検出

sentinel は、コマンド接続を作成したすべてのインスタンス (メイン サーバー) にメッセージを送信します。 ) 1 秒に 1 回、サーバー、他のセンチネルから) ping コマンドを送信し、インスタンスの応答を通じてインスタンスがオンラインかどうかを判断します。 有効な応答

: インスタンスは、PONG、-LOADING、および -MASTERDOWN のいずれかを返します。


無効な返信:上記3種類以外の返信、または指定時間内に返信がない場合。 インスタンスは、
down-after-milliseconds ミリ秒以内に無効な応答をセンチネルに返し続けます。次に、センチネルは、このインスタンスに対応するインスタンス構造を変更し、構造の flags 属性の SRI_S_DOWN フラグをオンにして、インスタンスが主観的オフライン状態に入ったことを示します。 (ミリ秒後のダウンはセンチネル設定ファイルで設定できます)
目標のオフライン ステータスを検出する

センチネルがいつ実行するかメイン サーバーが主観的にオフラインであると判断された場合、メイン サーバーが本当にオフラインであるかどうかを確認するために、同じくメイン サーバーを監視している他の監視員に対して、他の監視員もメイン サーバーがオフラインであると考えているかどうかを確認するように求められます。その数が一定数を超えると、メインサーバーは客観的にオフラインと判断されます。

他のセンチネルにサーバーをオフラインにすることに同意するかどうか尋ねます

SENTINEL is-master-down-by-addr

SENTINEL is-master-down-by-addr コマンドによるクエリ。パラメータの意味は次のとおりです:

SENTINEL is-master-down-by-addr コマンドを受信します Redis の高可用性ソリューションについて話しましょう。

他の Sentinel が SENTINEL is-master-down-by-addr コマンドを受信した後、 down-by-addr コマンドを実行すると、メイン サーバーの IP とポートに基づいて、メイン サーバーがオフラインかどうかを確認し、3 つのパラメータを含むマルチ バルク応答を返します。

Sentinel は、メイン サーバーがオフラインであることに同意する他の Sentinel の数をカウントします。設定された数に達すると、メイン サーバーの flags 属性の SRI_O_DOWN フラグがオンになります。がオンになり、メインサーバーがオフラインになっていることを示し、目的のオフライン状態に入ります。

10-Redis の高可用性ソリューションについて話しましょう。

リーダーの監視員を選出する

マスター サーバーが客観的にオフラインであると判断されると、オフラインのマスター サーバーを監視している各センチネルは交渉して新しいリーダー センチネルを選出し、このセンチネルがフェイルオーバー操作を実行します。

1Redis の高可用性ソリューションについて話しましょう。

マスターサーバーが目的のオフライン状態に入ったことを確認した後、SENTINEL is-master-down-by-addr コマンドが再度送信され、マスターサーバーを選択します。リーダーの番兵。

選出ル​​ール

  • 同じマスター サーバーを監視している複数のオンライン監視員はそれぞれ、リーダー監視員になることができます。
  • 各主要センチネルの選出後、選出が成功したかどうかに関係なく、すべてのセンチネルの設定エポック値が自動的に増加します。 (設定エポックは実際にはカウンターです)
  • 設定エポックでは、すべてのセンチネルは特定のセンチネルをローカル センチネルとして設定する機会を持ちます。この設定エポックで一度設定されると、変更することはできません。
  • マスター サーバーが客観的にオフラインであることを検出したすべてのセンチネルは、他のセンチネルに自分自身をローカル リーダー センチネルとして設定するよう依頼します。つまり、SENTINEL is-master-down-by-addr コマンドを送信して試行します。他のセンチネルに自分自身を設定してもらい、地元の有力なセンチネルになりましょう。
  • センチネルが SENTINEL is-master-down-by-addr コマンドを別のセンチネルに送信するとき、runid パラメーターの値が * ではなく、ソース センチネルの runid である場合、これは、ターゲットのセンチネルが自分自身をリードセンチネルとして設定することを意味します。
  • センチネルがローカル リーダーを設定するためのルールは 先着順です 最初のセンチネルがローカル リーダーとして設定されると、他のリクエストはすべて拒否されます。
  • SENTINEL is-master-down-by-addr コマンドを受信した後、ターゲット センチネルはソース センチネルにコマンド応答を返します。応答の leader_runid パラメータと leader_epoch パラメータは、ターゲット センチネルのローカル リーダー センチネルの runid と設定エポックをそれぞれ記録します。
  • ソース センチネルは応答を受信した後、返された設定エポックが自身の設定エポックと同じかどうかを比較します。同じであれば、返されたローカル先頭の runid が正しいかどうか比較を続けます。センチネルは自身の runid と同じであり、それらが一致している場合は、ターゲットのセンチネルが自身をローカル リーダーのセンチネルとして設定していることを意味します。
  • センチネルが半数以上のセンチネルによってローカルの主要なセンチネルとして設定されている場合、そのセンチネルは主要なセンチネルになります。
  • 主要なセンチネルは半数以上の人々のサポートが必要であり、各構成エポックで 1 回だけ設定できます。その場合、構成エポックには 1 つの主要なセンチネルのみが表示されます
  • If一定の制限時間内に、各センチネルがリーダー センチネルに選出された場合 (投票の半分を超える人がいない)、リーダー センチネルが選出されるまでの一定期間後に各センチネルが再度選出されます

フェイルオーバー

フェイルオーバーには、次の 3 つの手順が含まれます。

  • オフラインのマスターサーバーの場合は、スレーブサーバーを選択してメインサーバーに変換します。

  • オフラインのマスターサーバーの下にあるすべてのスレーブサーバーを新しいマスターサーバーにコピーします。

  • オフラインのマスター サーバーを新しいサーバーのスレーブ サーバーとして設定します。古いマスター サーバーがオンラインに戻ると、新しいマスター サーバーのスレーブ サーバーになります。

新しいマスター サーバーを選択します

オフライン マスター サーバーの下にあるすべてのスレーブ サーバー、スレーブを選択しますサーバーに SLAVEOF no one コマンドを送信し、スレーブ サーバーをマスター サーバーに変換します。

新しいマスター サーバーを選択するためのルール

先頭のセンチネルは、オフラインのマスター サーバーのすべてのスレーブ サーバーをリストに保存し、これを選択します。リストをフィルタリングして、新しいマスター サーバーを選択します。

  • リスト内のオフラインまたは接続されていないスレーブ サーバーをすべて削除します。

  • 過去 5 秒以内に先頭のセンチネルの INFO コマンドに応答しなかったリスト内のすべてのスレーブ サーバーを削除します。

  • すべて削除しますオフラインのサーバー dwon-after-milli秒 * 10 ミリ秒を超えて接続が切断されているサーバー

  • 次に、スレーブ サーバーの優先順位に従ってリスト内の残りのスレーブ サーバーを並べ替えます。その中から優先度が最も高いサーバーを選択します。

  • 同じ最も高い優先度を持つ複数のスレーブ サーバーがある場合、それらはレプリケーション オフセットに従って並べ替えられ、最大のオフセットを持つスレーブ サーバーが選択されます (最大のオフセットを持つスレーブ サーバー)。レプリケーション オフセットも保存されているデータが最新であることを意味します)

  • レプリケーション オフセットが同じ場合は、runid に従ってソートし、runid が最小のスレーブ サーバーを選択します

slaveof no one コマンドを送信した後、リーダー センチネルはアップグレードされたスレーブ サーバーに 1 秒に 1 回 (通常は 10 秒に 1 回)、info コマンドを送信します。元のスレーブからマスターに移行すると、リーダーの Sentinel はスレーブ サーバーがマスター サーバーにアップグレードされたことを認識します。

スレーブ サーバーのレプリケーション ターゲットを変更します

通过SLAVEOF命令来使从服务器复制新的主服务器。当sentinel监测到旧的主服务器重新上线后,也会发送SLAVEOF命令使它成为新的主服务器的从服务器。

sentinel总结

sentinel其实就是一个监控系统,,而sentinel监测到主服务器下线后,可以通过选举机制选出一个领头的sentinel,然后由这个领头的sentinel将下线主服务器下的从服务器挑选一个切换成主服务器,而不用人工手动切换。

集群

哨兵模式虽然做到了主从自动切换,但是还是只有一台主服务器进行写操作(当然哨兵模式也可以监视多个主服务器,但需要客户端自己实现负载均衡)。官方也提供了自己的方式实现集群。

节点

每个redis服务实例就是一个节点,多个连接的节点组成一个集群。

CLUSTER MEET <ip><port></port></ip>

向另一个节点发送CLUSTER MEET命令,可以让节点与目标节点进行握手,握手成功就能将该节点加入到当前集群。

启动节点

redis服务器启动时会根据cluster-enabled配置选项是否为yes来决定是否开启服务器集群模式。

1Redis の高可用性ソリューションについて話しましょう。

集群数据结构

每个节点都会使用一个clusterNode结构记录自己的状态,并为集群中其他节点都创建一个相应的clusterNode结构,记录其他节点状态。

typedef struct clusterNode {
    // 创建节点的时间
    mstime_t ctime; 
    // 节点的名称
    char name[CLUSTER_NAMELEN];
    // 节点标识
    // 各种不同的标识值记录节点的角色(比如主节点或从节点)
    // 以及节点目前所处的状态(在线或者下线)
    int flags;     
    // 节点当前的配置纪元,用于实现故障转移
    uint64_t configEpoch;
    // 节点的ip地址
    char ip[NET_IP_STR_LEN];  
    // 保存建立连接节点的有关信息
    clusterLink *link;          
    
    list *fail_reports;  
    // ...
} clusterNode;

clusterLink保存着连接节点所需的相关信息

typedef struct clusterLink {
    // ...
    // 连接的创建时间
    mstime_t ctime;           
    // 与这个连接相关联的节点,没有就为null
    struct clusterNode *node;   
    // ...
} clusterLink;

每个节点还保存着一个clusterState结构,它记录了在当前节点视角下,集群目前所处的状态,例如集群在线还是下线,集群包含多少个节点等等。

typedef struct clusterState {
    // 指向当前节点clusterNode的指针
    clusterNode *myself;  
    // 集群当前的配置纪元,用于实现故障转移
    uint64_t currentEpoch;
    // 集群当前的状态,上线或者下线
    int state;           
    // 集群中至少处理一个槽的节点数量
    int size;      
    // 集群节点的名单(包括myself节点)
    // 字典的键是节点的名字,字典的值为节点对应的clusterNode结构
    dict *nodes; 
} clusterState;

CLUSTER MEET 命令的实现

CLUSTER MEET <ip><port></port></ip>

  • 节点 A 会为节点 B 创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes 字典里面。

  • 之后,节点 A 将根据 CLUSTER MEET 命令给定的 IP 地址和端口号,向节点 B 发送一条 MEET 消息。

  • 如果一切顺利,节点 B 将接收到节点 A 发送的 MEET 消息,节点 B 会为节点 A 创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。

  • 之后,节点 B 将向节点 A 返回一条 PONG 消息。

  • 如果一切顺利,节点 A 将接收到节点 B 返回的 PONG 消息,通过这条 PONG 消息节点 A 可以知道节点 B 已经成功地接收到了自己发送的 MEET 消息。

  • 之后,节点 A 将向节点 B 返回一条 PING 消息。

  • 如果一切顺利,节点B将接收到节点A返回的PING消息,通过这条PING消息节点B知道节点A已经成功接收到自己返回的PONG消息,握手完成。

1Redis の高可用性ソリューションについて話しましょう。

槽指派

集群的整个数据库被分为16384个槽,每个键都属于16384个槽的其中一个,集群中每个节点处理0个或16384个槽。当所有的槽都有节点在处理时,集群处于上线状态,否则就是下线状态。

CLUSTER ADDSLOTS

CLUSTER ADDSLOTS <slot>...</slot>
通过CLUSTER ADDSLOTS命令可以将指定槽指派给当前节点负责,例如:CLUSTER ADDSLOTS 0 1 2 3 4 可以将0至4的槽指派给当前节点

记录节点的槽指派信息

clusterNode结构的slots属性和numslot属性记录了节点负责处理哪些槽:

typedef struct clusterNode {
         
    unsigned char slots[CLUSTER_SLOTS/8];
    
    int numslots;
    // ...
} clusterNode;

slots:是一个二进制数组,一共包含16384个二进制位。当二进制位的值是1,代表节点负责处理该槽,如果是0,代表节点不处理该槽numslots:numslots属性则记录节点负责处理槽的数量,也就是slots中值为1的二进制位的数量。

传播节点的槽指派信息

节点除了会将自己负责的槽记录在clusterNode中,还会将slots数组发送给集群中的其他节点,以此告知其他节点自己目前负责处理哪些槽。

typedef struct clusterState {
    clusterNode *slots[CLUSTER_SLOTS];
} clusterState;

slots包含16384个项,每一个数组项都是指向clusterNode的指针,表示被指派给该节点,如果未指派给任何节点,那么指针指向NULL。

CLUSTER ADDSLOTS命令的实现

1Redis の高可用性ソリューションについて話しましょう。

在集群中执行命令

客户端向节点发送与数据库有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽,并检查该槽是否指派给了自己。
如果指派给了自己,那么该节点直接执行该命令。如果没有,那么该节点会向客户端返回一个MOCED的错误,指引客户端转向正确的节点,并再次发送执行的命令。

1Redis の高可用性ソリューションについて話しましょう。

计算键属于那个槽

1Redis の高可用性ソリューションについて話しましょう。

CRC16(key)是计算出键key的CRC16的校验和,而 & 16383就是取余,算出0-16383之间的整数作为键的槽号。

判断槽是否由当前节点负责处理

计算出键所属的槽号i后,节点就能判断该槽号是否由自己处理。
如果clusterState.slots[i]等于如果clusterState.myself,那么由自己负责该节点可以直接执行命令。
如果不相等,那么可以获取clusterState.slots[i]指向如果clusterNode的ip和端口,向客户端返回MOVED错误,指引客户端转向负责该槽的节点。

集群模式下不会打印MOVED错误,而是直接自动转向。

重新分片

redis集群重新分配可以将任意数量已经指派给某个节点的槽改为指派给另一个节点,相关槽所属的键值对也会从源节点移动到目标节点。
重新分片操作是在线进行的,在重新分片的过程中,集群不用下线,源节点和目标节点都可以继续处理命令请求。 redis集群的重新分片操作是由redis-trib负责执行。重新分片执行步骤如下:

  • redis-trib对目标节点发送CLUSTER SETSLOT <slot> IMPORTING <source_id></source_id></slot>命令,让目标节点准备好从源节点导入槽slot的键值对。

  • redis-trib对源节点发送CLUSTER SETSLOT <slot> MIGRTING <target_id></target_id></slot>命令,让源节点准备好将属于槽slot的键值对迁移至目标节点。

  • redis-trib向源节点发送CLUSTER GETKEYSINSLOT <slot> <count></count></slot>命令,获取最多count个属于槽的键值对的键名称。

  • 对于步骤3获取的每个键名,redis-trib都向源节点发送一个MIGRTING <target_ip> <target_port> <key_name> 0 <timeout></timeout></key_name></target_port></target_ip>命令,将被选中的键值对从源节点迁移至目标节点。

  • 重复执行步骤3和步骤4,直到源节点保存的所以属于槽slot的键值对都被迁移至目标节点。

  • redis-trib向集群中任何一个节点发送CLUSTER SETSLOT <slot> NODE <target_id></target_id></slot>命令,将槽指派给目标节点。这一信息最终会通过消息发送至整个集群。

1Redis の高可用性ソリューションについて話しましょう。

CLUSTER SETSLOT IMPORTING 命令实现

typedef struct clusterState {
    // ...
    clusterNode *importing_slots_from[CLUSTER_SLOTS];

} clusterState;

importing_slots_from记录了当前节点正在从其他节点导入的槽。importing_slots_from[i]不为null,则指向CLUSTER SETSLOT <slot> IMPORTING <source_id></source_id></slot>命令,所代表的clusterNode结构。

CLUSTER SETSLOT MIGRTING 命令实现

typedef struct clusterState {
    // ...
    clusterNode *migrating_slots_to[CLUSTER_SLOTS];

} clusterState;

migrating_slots_to记录了当前节点正在迁移至其他节点的槽。migrating_slots_to[i]不为null,则指向迁移至目标节点所代表的clusterNode结构。

ASK错误

在重新分片期间,源节点向目标节点迁移槽的过程中,可能属于这个槽的一部分键值对一部分保存在源节点当中,而另一部分保存在目标节点当中。
客户端向源节点发送一个与数据库键有关的命令,恰好这个槽正在被迁移。
源节点现在自己的数据库中查找指定的键,如果找到,直接执行。
如果没有找到,节点会检查migrating_slots_to[i]查看键是否正在迁移,如果在迁移就返回一个ask错误,引导客户端转向目标节点。

ASKING

客户端收到ask错误之后,会先执行ASKING命令,再向目标节点发送命令。ASKING命令就是打开发送该命令的客户端的REDIS_ASKING标识。一般来说客户端发送的键如果不属于自己负责会返回MOVED错误(槽只迁移部分,这时槽还不属于目标节点负责),但还会检查importing_slots_from[i],如果显示节点正在导入槽i,并且发送命令的客户端带有REDIS_ASKING标识,那么它就会破例执行一次该命令。

1Redis の高可用性ソリューションについて話しましょう。

集群的故障转移

集群的故障转移效果和哨兵模式类似,也是将从节点升级成主节点。旧的主节点重新上线后将会成为新主节点的从节点。

故障检测

集群中每个节点会定期的向集群中其他节点发送PING消息,检测对方是否在线,如果指定时间内没有收到PONG消息,那么就将该节点标记为疑似下线。clusterState.nodes字典中找到该节点的clusterNode结构,将flags属性修改成REDIS_NODE_PFAIL标识。
集群中各个节点会互相发送消息来交换集群中各个节点的状态,例如:主节点A得知主节点B认为主节点C进入了疑似下线状态,主节点A会在clusterState.nodes字典中找到节点C的clusterNode结构,并将主节点B的下线报告添加到clusterNode结构的fail_reports链表当中。
每一个下线报告由一个clusterNodeFailReport结构表示

typedef struct clusterNodeFailReport {
    struct clusterNode *node; 
    // 最后一次收到下线报告的时间
    mstime_t time;            
} clusterNodeFailReport;

如果一个集群当中,半数以上负责处理槽的主节点都将某个主节点X报告为疑似下线。那么这个主节点X将被标记为已下线。将主节点X标记成已下线的节点会向集群广播一条关于主节点X的FAIL消息。所有收到这条FAIL消息的节点都会将主节点X标记成已下线。

故障转移

当一个从节点发现自己正在复制的主节点进入了已下线状态,从节点将开始对下线主节点进行故障转移。

  • 复制下线主节点的所有从节点,会有一个主节点被选中。

  • 被选中的从节点会执行SLAVEOF no one 命令,成为新的主节点。

  • 新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己。

  • 新的主节点向集群广播一条PONG消息,这条PONG消息可以让集群中的其他节点立即知道这个节点已经由从节点变成主节点。这个主节点已经接管了已下线节点负责处理的槽。

  • 新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成。

选举新的主节点

新的主节点通过选举产生

  • 集群的配置纪元是一个自增计数器,它的初始值为0。

  • 当集群的某个节点开始一次故障转移操作,集群的配置纪元的值加1。

  • 对于每个配置纪元,集群里每个负责处理槽的主节点都有一次投票的机会,第一个想主节点要求投票的从节点将获得主节点的投票。

  • 当从节点发现自己正在复制的主节点进入已下线状态时,从节点会向集群广播一条CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到这条消息,并具有投票权的主节点向这个从节点投票。

  • 如果一个主节点具有投票权(它正在负责处理槽),并且这个主节点尚未投票给其他从节点,那么主节点将向要求投票的从节点返回一条CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示这个主节点支持从节点成为新的主节点。

  • 每个参与选举的从节点都会接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,并根据自己收到了多少条这种消息来统计自己获得了多少主节点的支持。

  • 如果集群里有 N 个具有投票权的主节点,那么当一个从节点收集到大于等于 N / 2 + l 张支持票时,这个从节点就会当选为新的主节点。

  • 因为在每一个配置纪元里面,每个具有投票权的主节点只能投一次票,所以如果有 N 个主节点进行投票,那么具有大于等于 N / 2 + l 张支持票的从节点只会有一个,这确保了新的主节点只会有一个。

  • 如果在一个配置纪元里面没有从节点能收集到足够多的支持票,那么集群进人一个新的配置纪元,并再次进行选举,直到选出新的主节点为止。

主节点选举的过程和选举领头sentinel的过程非常相似。

数据丢失

主从复制数据丢失

主从复制之间是异步执行的,有可能master的部分数据还没来得及同步到从数据库,然后master就挂了,这时这部分未同步的数据就丢失了。

脑裂

脑裂就是说,某个master所在机器突然脱离了正常的网络,跟其他slave机器不能连接,但是实际上master还运行着。此时哨兵可能就会认为master 宕机了,然后开启选举,将其他slave切换成了master,这个时候,集群里面就会有2个master,也就是所谓的脑裂。
此时虽然某个slave被切换成了master,但是可能client还没来得及切换到新的master,还继续向旧master的写数据。
master再次恢复的时候,会被作为一个slave挂到新的master上去,自己的数据将会清空,重新从新的master复制数据,导致数据丢失。

减少数据丢失的配置

min-slaves-to-writ 1
min-slaves-max-lag 10

上述配置表示,如果至少有1个从服务器超过10秒没有给自己ack消息,那么master不再执行写请求。

主从数据不一致

当从数据库因为网络原因或者执行复杂度高命令阻塞导致滞后执行同步命令,导致数据同步延迟,造成了主从数据库不一致。

都看到这了,点个赞再走了吧:)

更多编程相关知识,请访问:编程入门!!

以上がRedis の高可用性ソリューションについて話しましょう。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。