>  기사  >  데이터 베이스  >  Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다!

Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다!

青灯夜游
青灯夜游앞으로
2022-01-17 10:07:032478검색

Redis를 위한 고가용성 솔루션은 무엇입니까? 이 기사는 Redis의 고가용성 솔루션을 소개합니다. 도움이 되길 바랍니다.

Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다!

Redis는 일반적으로 개별적으로 배포되지 않습니다. 그렇지 않으면 단일 실패 지점이 발생하지 않습니다. 그러면 Redis를 위한 고가용성 솔루션은 무엇입니까?

마스터-슬레이브 복제

사용자는 SLAVEOF 명령 또는 구성을 사용하여 한 서버가 다른 서버를 복제하도록 할 수 있습니다. 복제된 서버를 마스터 서버, 복제 서버를 슬레이브 서버라고 합니다. 이런 방식으로 마스터 서버에 키 값을 추가하고 동시에 슬레이브 서버에서 읽어옵니다. [관련 권장사항: Redis 동영상 튜토리얼]

복제 프로세스는 동기화와 명령 전파의 두 단계로 나뉩니다.

동기화

동기화는 서버의 데이터베이스 상태를 마스터 서버의 현재 데이터베이스 상태로 업데이트합니다.

클라이언트가 슬레이브 서버에 SLAVEOF 명령을 보내면 슬레이브 서버는 마스터 서버에 SYNC命令 동기화합니다. 단계는 다음과 같습니다.

  • 슬레이브 서버는 마스터 서버에 SYNC 명령을 보냅니다.

  • SYNC 명령을 받은 메인 서버는 BGSAVE 명령을 실행하고, 백그라운드에서 RDB 파일을 생성하며, 지금부터 실행되는 모든 쓰기 명령을 버퍼에 기록합니다.

  • 마스터 서버의 BGSAVE 명령이 실행된 후 마스터 서버는 BGSAVE에서 생성된 RDB 파일을 슬레이브 서버로 전송하고, 슬레이브 서버는 RDB 파일을 수신하여 로드하고, 슬레이브 서버의 데이터베이스 상태를 마스터에 업데이트합니다. 서버가 BGSAVE 명령을 실행할 때 데이터베이스 상태

  • 마스터 서버는 버퍼에 있는 모든 쓰기 명령을 슬레이브 서버로 보내고, 슬레이브 서버는 이러한 쓰기 명령을 실행하여 데이터베이스 상태를 현재 데이터베이스 상태로 업데이트합니다. 마스터 서버.

Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다!

명령 전파

동기화 작업이 완료된 후 마스터 서버와 슬레이브 서버의 데이터베이스 상태는 일치하지만 마스터 서버가 클라이언트 쓰기 명령을 받은 후 마스터 -slave Database 데이터 불일치가 다시 발생하고 명령 전파를 통해 데이터베이스 일관성이 달성됩니다.

PSYNC 동기화 최적화

2.8 이전 버전의 동기화는 매번 전체 동기화인데 서버가 잠시 연결이 끊긴 것 뿐이라면 사실 처음부터 동기화를 시작할 필요가 없고, 연결을 끊기만 하면 데이터가 동기화됩니다. 그래서 버전 2.8에서는 SYNC 명령 대신 PSYNC를 사용하기 시작했습니다.

PSYNC는 전체 동기화와 부분 동기화의 두 가지 상황으로 구분됩니다. 전체 동기화는 초기 동기화 상태를 처리하는 반면, 부분 동기화는 연결 끊김 및 재연결 상황을 처리합니다.

부분 동기화 구현

부분 동기화는 주로 다음 세 부분을 사용합니다.

  • 마스터 서버의 복제 오프셋과 슬레이브 서버의 복제 오프셋
  • 마스터 서버의 복제 백로그 버퍼
  • The 서버의 복제 오프셋 Running ID

복제 오프셋

마스터 서버의 복제 오프셋: 마스터 서버가 N바이트의 데이터를 슬레이브 서버에 전파할 때마다 자신의 복제 오프셋 +N 슬레이브의 복제 오프셋이 증가합니다. server: 슬레이브 서버가 마스터 서버가 전송한 N바이트의 데이터를 수신할 때마다 복제 오프셋이 +N만큼 증가합니다. 마스터 서버와 슬레이브 서버가 일관된 상태이면 오프셋은 항상 동일합니다. 오프셋이 동일하지 않으면 일관성이 없는 상태입니다.

복사 백로그 버퍼

복사 백로그 버퍼는 기본 서버에서 유지 관리하는 고정 길이 FIFO 대기열입니다. 기본 크기는 1MB입니다. 최대 길이에 도달하면 대기열에 추가되는 첫 번째 요소가 됩니다. 새로 추가된 요소를 배치하기 위해 배출되었습니다.
redis 명령이 전파되면 슬레이브 서버뿐만 아니라 복제 백로그 버퍼에도 전송됩니다.

Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다!

슬레이브 서버가 마스터 서버에 다시 연결되면 슬레이브 서버는 PSYNC 명령을 통해 복제 오프셋 오프셋을 마스터 서버로 보냅니다. 마스터 서버는 복제 오프셋에 따라 부분 동기화 또는 전체 동기화를 사용하기로 결정합니다. 오프셋 오프셋 이후의 데이터가 여전히 백로그 버퍼에 복사되는 경우 부분 동기화가 사용되고, 그렇지 않으면 전체 동기화가 사용됩니다.
(책에는 어떻게 판단하라고 나와있지 않습니다. 마스터 복사 오프셋에서 슬레이브 복사 오프셋을 뺀 값이어야 할 것 같습니다. 1MB보다 크다면 버퍼 백로그에 없는 데이터가 있다는 뜻인가요?)

서버의 실행 ID

서버가 시작되면 서버 실행 ID로 40자리 임의의 문자가 생성됩니다.

슬레이브 서버가 마스터 서버에 처음 복제할 때 마스터 서버는 자신의 실행 ID를 슬레이브 서버로 전송하고 슬레이브 서버는 이 실행 ID를 저장합니다. 슬레이브 서버 연결이 끊어졌다가 다시 연결되면, 슬레이브 서버에 저장된 런닝 ID가 현재 마스터 서버의 런닝 ID와 동일할 경우 부분 동기화를 시도합니다. 다르면 전체 동기화가 수행됩니다.

PSYNC의 전체 프로세스

Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다!

심장박동 감지

명령 전파 단계에서 슬레이브 서버는 기본적으로 초당 1회의 빈도로 마스터 서버에 명령을 보냅니다.
REPLICONF ACK <replication_offset></replication_offset>
replication_offset은 서버의 현재 복제 오프셋입니다. REPLICONF ACK 명령 전송에는 마스터 및 슬레이브 서버에 대한 세 가지 기능이 있습니다.

  • 마스터 및 슬레이브 서버의 네트워크 연결 상태를 감지합니다.

  • 최소 슬레이브 옵션의 보조 구현.

  • 명령 손실을 감지합니다.

마스터-슬레이브 서버의 네트워크 연결 상태 감지

마스터-슬레이브 서버는 REPLICONF ACK 명령을 송수신하여 둘 사이의 네트워크 연결이 정상인지 확인할 수 있습니다. 서버가 보낸 REPLICONF ACK 명령을 1초 이상 슬레이브 서버로부터 메시지를 받으면 마스터 서버는 마스터와 슬레이브 사이에 문제가 있음을 알게 됩니다.

min-slaves 옵션의 보조 구현

redis의 min-slaves-to-writemin-slaves-max-lag두 가지 옵션은 안전하지 않은 상황에서 마스터-슬레이브 서버가 쓰기 명령을 실행하는 것을 방지할 수 있습니다.

min-slaves-to-write 3
min-slaves-max-lag 10

위와 같이 구성하면 슬레이브 서버 수가 3개 미만이거나 슬레이브 서버 3개 모두의 지연이 10초 이상인 경우 마스터 서버가 쓰기 명령 실행을 거부한다는 의미입니다. .

명령 손실 감지

마스터 서버에서 슬레이브 서버로 전송된 쓰기 명령이 네트워크 장애로 인해 중간에 손실된 경우, 슬레이브 서버가 마스터 서버로 REPLICONF ACK 명령을 보내면 마스터 서버는 슬레이브 서버의 현재 복제 오프셋 검색 오프셋이 자신의 오프셋보다 작을 경우 마스터 서버는 슬레이브 서버의 복제 오프셋을 기반으로 복제 버퍼에서 슬레이브 서버에서 누락된 데이터를 찾아 데이터를 다시 작성하여 전송할 수 있습니다. 슬레이브 서버로 보냅니다.

마스터-슬레이브 복제 요약

사실 마스터-슬레이브 복제는 데이터의 추가 복사본입니다. 지속성을 위한 RDB 및 AOF가 있어도 마스터 서버의 전체 시스템이 중단될 수 있고, 마스터-슬레이브 복제 가능 마스터 서버와 슬레이브 서버가 서로 다른 두 대의 시스템에 배포되므로 마스터 서버 시스템이 중단되더라도 수동으로 슬레이브 서버로 전환하여 서비스를 계속할 수 있습니다.

sentinel

마스터-슬레이브는 데이터 백업을 구현하지만 마스터 서버가 중단되면 슬레이브 서버에서 마스터 서버로 수동으로 전환해야 합니다. Sentinel은 기본 서버가 중단되면 자동으로 보조 서버에서 기본 서버로 전환할 수 있습니다.

Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다!

서버1이 현재 오프라인 상태라고 가정하면 센티넬 시스템은 모든 마스터 및 슬레이브 서버를 모니터링할 수 있습니다. server1의 오프라인 시간이 사용자가 설정한 오프라인 시간의 상한을 초과하면 sentinel 시스템은 server1의 장애 조치를 수행합니다.

  • 먼저 sentinel 시스템은 server1 아래의 슬레이브 서버 중 하나를 선택하고 이 선택된 슬레이브를 전송합니다. 서버가 새로운 기본 서버로 업그레이드됩니다.

  • 이후 센티넬 시스템은 server1 아래의 모든 슬레이브 서버에 새로운 복제 명령을 보내 해당 서버가 새로운 마스터 서버의 슬레이브 서버가 될 수 있도록 합니다. 모든 슬레이브 서버가 새로운 마스터 서버로 복제되면 장애 조치 작업이 완료됩니다.

  • 또한 센티널은 오프라인 서버1도 모니터링하고, 다시 온라인 상태가 되면 새 마스터 서버의 슬레이브 서버로 설정합니다.

sentinel 상태 초기화

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;

sentinel 상태의 masters 속성을 초기화합니다.

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;

메인 서버에 대한 네트워크 연결 생성

센티넬 초기화의 마지막 단계는 모니터링되는 메인 서버에 대한 네트워크 연결을 생성하는 것입니다.

Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다!

명령 연결: 구체적으로 메인 서버에 명령을 보내고 명령 응답을 받습니다.
구독 연결: 메인 서버의 _sentinel_:hello 채널을 구독하는 데 특별히 사용됩니다.

메인 서버 정보 가져오기

sentinel은 기본적으로 10초마다 명령 연결을 통해 모니터링되는 메인 서버에 INFO 명령을 보내고, 응답을 통해 메인 서버의 현재 정보를 가져옵니다. 다음 정보를 얻으려면 회신하세요.

  • 마스터 서버의 run_id
  • 마스터 서버 아래 모든 슬레이브 서버의 정보입니다.

sentinelRedisInstance 아래의 이름 사전과 runid 필드는 이 정보를 기반으로 업데이트될 수 있습니다.

슬레이브 서버 정보 가져오기

센티넬은 슬레이브 서버에 대한 명령 연결 및 구독 연결도 생성합니다.

Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다!

sentinel은 기본적으로 10초마다 명령 연결을 통해 슬레이브 서버에 INFO 명령을 보내고, 응답을 통해 슬레이브 서버의 현재 정보를 가져옵니다. 답변은 다음과 같습니다.

Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다!

  • 슬레이브 서버의 실행 ID
  • 슬레이브 서버의 역할
  • 마스터 서버의 IP 및 포트
  • 마스터 서버의 연결 상태 master_link_status
  • 슬레이브 서버의 우선순위slav_priority
  • 슬레이브 서버 복사 오프셋 변수

info의 응답 정보에 따라 sentinel은 슬레이브 서버의 인스턴스 구조를 업데이트할 수 있습니다.

마스터 및 슬레이브 서버의 구독 연결에 정보 보내기

기본적으로 센티널은 모니터링되는 마스터 및 슬레이브 서버에 2초에 한 번씩 명령을 보냅니다.

Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다!

s_ip:sentinel의 IP 주소 s_ip:sentinel的ip地址
s_port:sentinel的端口号
s_runid:sentinel的运行id
s_epoch:sentinel当前的配置纪元
m_name:主服务器的名字
m_ip:主服务器的ip地址
m_port:主服务器的端口号
m_epoch:主服务器当前的配置纪元
向sentinel_:hello频道发送信息,也会被监视同一个服务器的其他sentinel监听到(包括自己)。

创建连向其他sentinel的命令连接

sentinel之间会互相创建命令连接。监视同一个嘱咐其的多个sentinel将形成相互连接的网络。

Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다!

sentinel之间不会创建订阅连接。

检测主观下线状态

sentinel会每秒一次向所有与它创建了命令连接的实例(主服务器、从服务器、其他sentinel)发送ping命令,通过实例的回复来判断实例是否在线。
有效回复:实例返回+PONG、-LOADING、-MASTERDOWN其中一种。
无效回复:以上三种回复之外的其他回复,或者指定时长内没回复。
某个实例在down-after-milliseconds毫秒内,连续向sentinel返回无效回复。那么sentinel就会修改这个实例对应的实例结构,在结构的flags属性中打开SRI_S_DOWN标识,标识该实例进入主观下线状态。(down-after-milliseconds可以在sentinel的配置文件中配置)

检测客观下线状态

当sentinel将一个主服务器判断为主观下线后,为了确认这个主服务器是否真的下线,还会想其他同样监视这个主服务器的其他sentinel询问,看其他sentinel是否也认为该主服务器下线了。超过一定数量就将主服务器判断为客观下线。

询问其他sentinel是否同意该服务器下线

SENTINEL is-master-down-by-addr <ip><port><current_epoch><runid></runid></current_epoch></port></ip>s_port:sentinel의 포트 번호

s_runid:sentinel의 실행 ID
s_epoch code>: Sentinel의 현재 구성 에포크 <p><code>m_name: 메인 서버의 이름

m_ip: 메인 서버의 IP 주소
m_port: 메인 서버의 포트 번호

m_epoch: 메인 서버의 현재 구성 에포크Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다! sentinel_:hello 채널로 정보를 보내는 것은 동일한 서버를 모니터링하는 다른 센티널(자신 포함)에서도 모니터링됩니다.

다른 센티널에 대한 명령 연결 만들기

센티널은 서로에 대한 명령 연결을 만듭니다. 동일한 명령을 모니터링하는 여러 센티널은 상호 연결된 네트워크를 형성합니다.

Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다!10-Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다!

센티널 간에는 구독 연결이 생성되지 않습니다.

주관적인 오프라인 상태 감지

🎜🎜센티넬은 명령 연결을 생성한 모든 인스턴스(마스터 서버, 슬레이브 서버, 기타 센티널)에 1초마다 ping 명령을 보내고 이를 통해 인스턴스를 판단합니다. 인스턴스의 답변 온라인인가요? 🎜🎜유효한 응답🎜: 인스턴스는 +PONG, -LOADING 및 -MASTERDOWN 중 하나를 반환합니다. 🎜🎜잘못된 답변🎜: 위 3가지 유형 이외의 답변 또는 지정된 시간 내에 답변이 없는 경우입니다. 🎜인스턴스는 down-after-milliseconds밀리초 내에 계속해서 잘못된 응답을 센티넬에 반환합니다. 그런 다음 Sentinel은 이 인스턴스에 해당하는 인스턴스 구조를 수정하고 구조의 플래그 속성에서 SRI_S_DOWN 플래그를 켜서 인스턴스가 주관적 오프라인 상태에 진입했음을 나타냅니다. (down-after-milliseconds는 sentinel 구성 파일에서 구성 가능) 🎜🎜🎜🎜객관적인 오프라인 상태 감지🎜🎜🎜🎜🎜Sentinel이 메인 서버가 주관적으로 오프라인이라고 판단할 때, 메인 서버가 실제로 오프라인인지 확인하기 위해 오프라인 상태가 되면 이 주 서버를 모니터링하고 있는 다른 센티널에게도 요청하여 다른 센티널도 주 서버가 오프라인이라고 생각하는지 확인합니다. 해당 개수가 일정 개수를 초과할 경우, 메인서버는 객관적으로 오프라인으로 판단됩니다. 🎜🎜🎜🎜다른 센티널에게 서버가 오프라인 상태인 것에 동의하는지 물어보세요🎜🎜🎜SENTINEL is-master-down-by-addr <ip><port><current_epoch><runid></runid></current_epoch></port></ip>🎜 🎜🎜SENTINEL is-master-down-by-addr 명령을 통해 묻습니다. 매개변수의 의미는 다음과 같습니다. 🎜🎜🎜🎜🎜🎜🎜SENTINEL is-master-down-by-addr 명령을 받습니다. 🎜🎜 🎜다른 센티넬은 SENTINEL is를 수신합니다. - master-down-by-addr 명령이 실행된 후 마스터 서버의 IP와 포트를 기반으로 마스터 서버가 오프라인인지 확인한 후 3개가 포함된 Multi Bulk 응답을 반환합니다. 매개변수. 🎜🎜🎜🎜🎜sentinel은 기본 서버가 오프라인 상태임을 동의하는 다른 Sentinel의 수를 계산합니다. 구성된 숫자에 도달하면 기본 서버의 플래그 속성에 있는 SRI_O_DOWN 플래그가 켜져서 기본 서버가 오프라인 상태임을 나타냅니다. 객관적인 오프라인 상태에 들어갔습니다. 🎜🎜🎜🎜리더 파수꾼을 선출하세요🎜🎜🎜

마스터 서버가 객관적으로 오프라인이라고 판단되면 오프라인 마스터 서버를 모니터링하는 각 센티널은 협상을 통해 새로운 리더 센티넬을 선출하게 되며, 이 센티널이 장애 조치 작업을 수행하게 됩니다.

1Redis의 고가용성 솔루션에 대해 이야기해 보겠습니다!

마스터 서버가 목표 오프라인 상태로 진입한 것을 확인한 후 SENTINEL is-master-down-by-addr 명령이 다시 전송되어 리더 센티넬을 선출합니다.

선거 규칙

  • 동일한 마스터 서버를 모니터링하는 여러 온라인 센티널 각각이 리더 센티널이 될 수 있습니다.
  • 각 리더 센티넬 선출 후, 선거의 성공 여부에 관계없이 모든 센티넬의 구성 에포크 값이 자동으로 증가합니다. (구성 에포크는 실제로 카운터입니다.)
  • 구성 에포크에서는 모든 센티널이 특정 센티널을 로컬 센티널로 설정할 수 있습니다. 이 구성 에포크에서 설정한 후에는 변경할 수 없습니다.
  • 마스터 서버가 객관적으로 오프라인임을 발견한 모든 센티널은 다른 센티널에게 자신을 로컬 주요 센티널로 설정하도록 요청합니다. 즉, SENTINEL is-master-down-by-addr 명령을 보내 다른 센티널이 오프라인이 되도록 시도합니다. 스스로를 지역의 선도적인 파수꾼으로 삼았습니다.
  • Sentinel이 SENTINEL is-master-down-by-addr 명령을 다른 Sentinel에게 보낼 때, runid 매개변수의 값이 *가 아니고 소스 sentinel의 runid라면 이는 다음을 의미합니다. 대상 센티넬이 필요하다는 것을 자신을 리드 센티넬로 설정하세요. SENTINEL is-master-down-by-addr命令时,如果runid参数的值不是*,而是源sentinel的runid,就表示要目标sentinel将自己设置成领头sentinel。
  • sentinel设置局部领头的规则是先到先得
  • 센티넬이 로컬 리더를 설정하는 규칙은 선착순입니다. 첫 번째 센티넬이 로컬 리더로 설정된 후에는 다른 모든 요청이 거부됩니다.
  • SENTINEL is-master-down-by-addr 명령을 받은 후 대상 센티널은 소스 센티널에 명령 응답을 반환합니다. 응답의 Leader_runid 매개변수와 Leader_epoch 매개변수는 각각 대상 센티널의 로컬 리더 센티널의 runid와 구성 epoch를 기록합니다.
  • 소스 센티널은 응답을 받은 후 반환된 구성 에포크가 자신의 구성 에포크와 동일한지 비교합니다. 그렇다면 반환된 로컬 선도 센티널의 runid가 자신의 것과 동일한지 계속 비교합니다. runid가 일치하면 대상 센티널이 자신을 로컬 선두 센티널로 설정한다는 의미입니다.
  • 한 센티넬이 절반 이상의 센티널로 지역 선두 센티넬로 설정되면 해당 센티넬이 선두 센티널이 됩니다.
  • 리딩 센티넬은 절반 이상의 지원이 필요하며 각 구성 에포크에서 한 번만 설정할 수 있습니다. 그런 다음 구성 에포크에서는 하나의 리딩 센티널만 나타납니다
  • 특정 시간 내에 각 센티널이 선출됩니다. 선두 센티넬(어떤 사람도 절반 이상의 표를 얻지 못한 경우), 각 센티넬은 선두 센티넬이 선출될 때까지 일정 시간 후에 다시 선출됩니다

Failover

Failover에는 다음이 포함됩니다. 세 단계:
  • 오프라인 상태가 된 후 마스터 서버 아래의 모든 슬레이브 서버 중 슬레이브 서버를 선택하여 마스터 서버로 전환합니다.
  • 오프라인 마스터 서버 아래의 모든 슬레이브 서버를 새로운 마스터 서버로 복사하도록 합니다.
  • 오프라인 마스터 서버를 새 서버의 슬레이브 서버로 설정하세요. 기존 마스터 서버가 다시 온라인 상태가 된 후 새 마스터 서버의 슬레이브 서버가 됩니다.

새 마스터 서버 선택

오프라인 마스터 서버 아래의 모든 슬레이브 서버 중에서 슬레이브 서버를 선택하고, 슬레이브 서버에 SLAVEOF no one 명령을 보내 슬레이브 서버를 변환하여 마스터가 됩니다. 섬기는 사람.

새 마스터 서버 선택 규칙

리딩 센티널은 오프라인 마스터 서버의 모든 슬레이브 서버를 목록에 저장한 후 이 목록을 필터링하여 새 마스터 서버를 선택합니다.
  • 목록에서 오프라인이거나 연결이 끊긴 슬레이브 서버를 모두 삭제하세요.
  • 최근 5초 동안 선두 센티넬의 INFO 명령에 응답하지 않은 목록의 모든 슬레이브 서버를 삭제합니다.
  • dwon-after-milliseconds 이상 오프라인 서버와의 연결이 끊어진 모든 서버를 삭제합니다. * 10ms
  • 그런 다음 목록에 남아 있는 슬레이브 서버를 슬레이브 서버의 우선순위에 따라 정렬하고 우선순위가 가장 높은 서버를 선택합니다.
  • 동일한 우선 순위가 가장 높은 슬레이브 서버가 여러 개 있는 경우 복제 오프셋에 따라 정렬하고 오프셋이 가장 큰 슬레이브 서버를 선택합니다. (복제 오프셋이 가장 크다는 것은 저장하는 데이터가 최신임을 의미하기도 합니다)
  • 복제 오프셋이 동일하면 runid에 따라 정렬하고 가장 작은 runid를 가진 슬레이브 서버를 선택합니다.

slaveofnoone 명령을 보낸 후 선두 센티널은 업그레이드된 슬레이브 서버에 한 번씩 정보를 보냅니다. 두 번째 명령(보통 10초마다), 반환된 응답 역할이 원래 슬레이브에서 마스터로 변경되면 선두 센티널은 슬레이브 서버가 마스터 서버로 업그레이드되었음을 알게 됩니다.

슬레이브 서버의 복제 대상 수정

🎜

通过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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.cn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제