이 글에서는 Redis배포 관련 지식을 소개하고 마스터-슬레이브 복제, Sentinel 및 클러스터링을 안내하여 Redis 수준을 한 단계 끌어올릴 수 있습니다!
마스터-슬레이브 복제는 Redis 배포의 초석이자 Redis 고가용성을 보장합니다. Redis에서는 복제되는 서버를 마스터 서버(Master), 마스터 서버를 복제하는 서버를 슬레이브 서버(Slave)라고 합니다. [관련 권장사항: Redis 동영상 튜토리얼]
마스터-슬레이브 복제 구성은 세 가지 방법이 있습니다(IP-메인 서버 IP 주소/PORT-메인 서버 Redis 서비스 포트):
구성 파일 ——redis.conf 파일에서 슬레이브of ip 포트를 구성합니다
명령 ——레디스 클라이언트를 입력하여 슬레이브of ip 포트를 실행합니다
시작 매개변수 ——./redis-server --slaveof ip port
Redis의 마스터-슬레이브 복제 메커니즘은 처음에는 6.x 버전만큼 완벽하지는 않았지만 버전마다 반복되었습니다. 일반적으로 세 가지 버전의 반복을 거쳤습니다.
서버로부터 수신 클라이언트가 보낸 슬레이브of ip prot 명령에 대해 슬레이브 서버는 ip:port 기반으로 마스터 서버에 소켓 연결을 생성합니다.
2.1.3 결함
위에서 보면 2.8 이전 버전의 마스터-슬레이브 복제에는 결함이 보이지 않습니다. 이는 네트워크 변동을 고려하지 않았기 때문입니다. 분산을 이해하는 형제라면 CAP 이론을 들어봤을 것입니다. CAP 이론은 분산 스토리지 시스템의 초석입니다. CAP 이론에는 P(파티션 네트워크 파티션)가 있어야 하며 Redis 마스터-슬레이브 복제도 예외는 아닙니다. 마스터 서버와 슬레이브 서버 사이에 네트워크 장애가 발생하여 일정 시간 동안 슬레이브 서버와 마스터 서버 간의 통신이 중단된 경우 슬레이브 서버가 마스터 서버에 다시 연결되는 경우 마스터 서버의 데이터 상태 이 기간 동안 마스터-슬레이브 서버가 변경되면 서버 간에 데이터 상태 불일치가 발생합니다. Redis 2.8 이전의 마스터-슬레이브 복제 버전에서 이 데이터 상태 불일치를 해결하는 방법은 sync 명령을 다시 보내는 것입니다. 동기화를 통해 마스터 서버와 슬레이브 서버의 데이터 상태가 일관되도록 보장할 수 있지만 동기화는 리소스를 많이 소모하는 작업임이 분명합니다.
sync 명령 실행, 마스터 및 슬레이브 서버에 필요한 리소스:
마스터 서버는 BGSAVE를 실행하여 RDB 파일을 생성하며, 이는 많은 CPU, 디스크 I/O 및 메모리 리소스를 차지합니다
The 생성된 RDB 파일을 마스터 서버가 슬레이브 서버에 보내면 네트워크 대역폭을 많이 차지하게 됩니다.
서버에서 RDB 파일을 받아 로드하면 슬레이브 서버가 차단되어 서비스를 제공할 수 없게 됩니다
위의 세 가지 사항에서 볼 수 있듯이 sync 명령은 마스터의 응답 능력 저하로 인해 슬레이브 서버가 이 기간 동안 외부 서비스 제공을 거부하게 만들 수도 있습니다.
2.2.1 개선
2.8 이전 버전의 경우 Redis는 2.8 이후 서버에서 다시 연결한 후 데이터 상태 동기화를 개선했습니다. 개선 방향은 전체 재동기화 발생을 줄이고, 부분 재동기화를 최대한 활용하는 것입니다. 버전 2.8 이후에는 sync 명령 대신 psync 명령이 사용되어 동기화 작업을 수행합니다. psync 명령에는 전체 동기화 기능과 증분 동기화 기능이 모두 있습니다.
전체 동기화는 이전 버전(동기화)과 일치합니다.
증분 동기화 연결 끊김 및 재연결 후 복제의 경우 상황에 따라 다른 조치가 취해지며, 서비스에서 누락된 데이터 중 일부만 계속 전송됩니다.
2.2.2 psync 구현 방법
서버와의 연결이 끊어졌다가 다시 연결된 후 증분 동기화를 달성하기 위해 Redis는 세 가지 보조 매개변수를 추가합니다:
복제 오프셋(복제 오프셋)
백로그 버퍼( 복제 백로그)
서버 실행 ID(run id)
2.2.2.1 복제 오프셋
복제 오프셋은 마스터 서버와 슬레이브 서버에서 유지됩니다
마스터 서버는 데이터를 다음으로 보냅니다. 슬레이브 서비스는 N바이트의 데이터를 확산시키며, 마스터 서비스의 복제 오프셋은 N
증가합니다. 슬레이브 서버는 마스터 서버가 보낸 데이터를 수신하고, N바이트의 데이터를 수신하며, 슬레이브 서버는 복제 오프셋 N
일반적인 동기화 상황은 다음과 같습니다.
마스터 서버와 슬레이브 서버 간의 복제 오프셋이 같은지 비교하면 마스터 서버와 슬레이브 서버 간의 데이터 상태가 같은지 알 수 있습니다. 일관성을 유지하세요. 이때 A/B가 정상적으로 전파되고 슬레이브 서버 C의 연결이 끊어졌다고 가정하면, 다음과 같은 상황이 발생합니다.
분명히 복제 오프셋에 따라 슬레이브 서버 C의 연결이 끊어졌다가 다시 연결된 후, 마스터 서버는 서버에서 누락된 100바이트의 데이터만 보냅니다. 하지만 마스터 서버는 슬레이브 서버에서 누락된 데이터가 무엇인지 어떻게 알 수 있나요?
2.2.2.2 백로그 복사 버퍼
백로그 복사 버퍼는 기본 크기가 1MB인 고정 길이 대기열입니다. 마스터 서버의 데이터 상태가 변경되면 마스터 서버는 데이터를 슬레이브 서버와 동기화하고 복사본을 복제 백로그 버퍼에 저장합니다.
오프셋을 일치시키기 위해 복사 백로그 버퍼는 데이터 내용을 저장할 뿐만 아니라 각 바이트에 해당하는 오프셋도 기록합니다.
슬레이브 서버의 연결이 끊겼다가 다시 연결되면 마지막으로 슬레이브는 서버는 psync 명령을 통해 자체 복제 오프셋(오프셋)을 마스터 서버로 보냅니다. 마스터 서버는 이 오프셋을 사용하여 증분 전파를 수행할지 아니면 전체 동기화를 수행할지 결정할 수 있습니다.
오프셋 오프셋+1의 데이터가 아직 복사 백로그 버퍼에 있으면 증분 동기화 작업을 수행합니다.
그렇지 않으면 동기화와 일치하는 전체 동기화 작업을 수행합니다.
Redis의 기본 복사 백로그 버퍼 크기는 1MB입니다. 사용자 정의해야 하는 경우 어떻게 설정하나요? 당연히 증분 동기화를 최대한 사용하고 싶지만 버퍼가 너무 많은 메모리 공간을 차지하는 것을 원하지 않습니다. 그런 다음 Redis 슬레이브 서비스 연결이 끊어진 후 다시 연결되는 시간 T와 Redis 마스터 서버가 초당 수신하는 쓰기 명령의 메모리 크기 M을 추정하여 복제 백로그 버퍼 S의 크기를 설정할 수 있습니다.
S = 2 * M * T
여기서 2배 확장은 대부분의 연결 해제 및 재연결이 증분 동기화를 사용할 수 있도록 일정량의 공간을 남겨 두는 것입니다.
2.2.2.3 ID를 실행하는 서버
이것을 보고 나면 위에서 연결 끊김과 재연결의 증분 동기화가 가능하다는 것을 다시 생각하게 되는데 왜 아직도 ID를 실행해야 합니까? 실제로 고려하지 못한 또 다른 상황이 있습니다. 즉, 마스터 서버가 다운되면 슬레이브 서버가 새로운 마스터 서버로 선출되는 것입니다. 이 경우에는 실행 중인 ID를 비교하여 구별할 수 있습니다.
실행 ID(run id)는 서버가 시작될 때 자동으로 생성되는 임의의 16진수 문자열 40개입니다. 마스터 서비스와 슬레이브 서버 모두 실행 ID를 생성합니다.
슬레이브 서버가 마스터 서버의 ID를 동기화할 때 처음 데이터가 생성되면 마스터 서버는 자신의 실행 ID를 슬레이브 서버로 보내고 슬레이브 서버는 이를 RDB 파일에 저장합니다.
슬레이브 서버의 연결이 끊겼다가 다시 연결되면 슬레이브 서버가 이전에 저장된 마스터 서버의 운영 ID를 마스터 서버에 적용하며, 서버 운영 ID가 일치하면 메인 서버가 변경되지 않은 것으로 증명되므로 증분 동기화를 시도해 볼 수 있습니다
서버 운영 ID가 일치하지 않으면 전체 동기화를 시도하세요. 동기화
2.2.3 완료 ppsync
완료 psync 프로세스는 매우 복잡하며 마스터-슬레이브 복제 버전 2.8-4.0에서 매우 완벽해졌습니다. psync 명령으로 전송되는 매개 변수는 다음과 같습니다.
psync
슬레이브 서버가 마스터 서버를 복제하지 않은 경우(마스터 서버가 변경될 수 있으므로 마스터-슬레이브가 처음 복제하는 것은 아니며, 그러나 슬레이브 서버 동기화의 첫 번째 전체 복사본) 슬레이브 서버는 다음을 보냅니다:
psync ? -1
전체 psync 프로세스는 다음과 같습니다.
SLAVEOF 127.0.0.1이 서버에서 수신됨 6379
명령은 서버에서 명령 개시자에게 OK를 반환합니다(이것은 비동기 작업이므로 먼저 OK를 반환한 다음 주소 및 포트 정보를 저장합니다)
저장 서버에서 마스터 호스트 및 마스터 포트로 IP 주소 및 포트 정보
슬레이브 서버는 마스터 호스트 및 마스터 포트를 기반으로 마스터 서버에 대한 소켓 연결을 적극적으로 시작합니다. 파일 복사에 특별히 사용되는 파일 이벤트 핸들러를 후속 사용을 위해 이 소켓 연결과 연결합니다. (마스터-슬레이브 복제에서 마스터 서버와 슬레이브 서버는 실제로 서로의 클라이언트이자 서버입니다.)
슬레이브 서버가 적극적으로 PING 명령을 보냅니다. 마스터 서버가 지정된 시간 초과 기간 내에 PONG을 반환하면 소켓 연결이 가능하다는 것을 증명하고, 그렇지 않으면 연결이 끊긴 후 다시 연결됩니다. 인증을 위해 마스터 서버에 AUTH masterauth 명령을 보냅니다. 슬레이브 서버가 비밀번호를 보내지만 마스터 서비스가 비밀번호를 설정하지 않은 경우 마스터 서버는 비밀번호가 설정되지 않음 오류를 보냅니다. 마스터 서버가 비밀번호를 요구하지만 슬레이브 서버가 비밀번호를 보내지 않으면 마스터 서버는 서버는 NOAUTH 오류를 보냅니다. 비밀번호가 일치하지 않으면 마스터 서버는 잘못된 비밀번호 오류를 보냅니다.
슬레이브 서버는 REPLCONF 수신 포트 xxxx(xxxx는 슬레이브 서버의 포트를 나타냄)를 마스터 서버로 보냅니다. 명령을 받은 후 메인 서버는 데이터를 저장합니다. 클라이언트가 INFO 복제를 사용하여 마스터-슬레이브 정보를 쿼리하면 데이터를 반환할 수 있습니다
서버에서 psync 명령을 보내세요. 위 그림에서 psync의 두 가지 상황
마스터 서버와 슬레이브 서버는 서로 클라이언트로 데이터 요청/응답을 수행합니다
마스터 서버와 슬레이브 서버는 하트비트 패킷 메커니즘을 사용하여 여부를 결정합니다. 연결이 끊어졌습니다. 슬레이브 서버는 1초마다 REPLCONF ACL 오프셋(슬레이브 서버의 복제 오프셋) 명령을 보냅니다. 이 메커니즘은 마스터와 슬레이브 간의 올바른 데이터 동기화를 보장할 수 있습니다. 서버는 마스터와 슬레이브 간의 일관된 데이터 상태를 보장하기 위해 증분/전체 동기화 조치를 취합니다(증분/전체 선택은 오프셋+1의 데이터가 여전히 복제 백로그 버퍼에 있는지 여부에 따라 달라집니다)
Redis 버전 2.8-4.0에는 아직 개선의 여지가 남아있습니다. 메인 서버 전환 시 증분 동기화를 수행할 수 있나요? 따라서 Redis 4.0 버전은 이 문제를 해결하기 위해 최적화되었으며 psync는 psync2.0으로 업그레이드되었습니다. pync2.0은 서버 실행 ID를 버리고 대신 replid와 replid2를 사용했습니다. Replid는 현재 메인 서버의 실행 ID를 저장하고, replid2는 이전 메인 서버의 실행 ID를 저장합니다.
복제 오프셋
복제 백로그
마스터 서버 실행 ID(replid)
마지막 마스터 서버 실행 ID(replid2)
replid 및 2 우리는 증분 문제를 해결할 수 있습니다 메인 서버 전환 시 동기화:
replid가 현재 메인 서버의 실행 ID와 같을 경우 동기화 방법 증분/전체 동기화를 결정합니다.
replid가 같지 않으면 Replicad2가 동일함(이전 마스터 서버의 슬레이브 서버에 속해 있는지 여부) 동일하면 증분/전체 동기화를 선택할 수 있습니다. 동일하지 않으면 전체 동기화만 수행할 수 있습니다.
마스터-슬레이브 복제는 Redis 배포의 기반을 마련하지만 일반적인 마스터-슬레이브 복제는 고가용성을 달성할 수 없습니다. 일반적인 마스터-슬레이브 복제 모드에서 마스터 서버가 다운되면 운영 및 유지 관리 담당자는 마스터 서버를 수동으로 전환할 수만 있습니다. 분명히 이 솔루션은 권장되지 않습니다. 위 상황에 대응하여 Redis는 노드 장애에 저항할 수 있는 고가용성 솔루션인 Redis Sentinel을 공식 출시했습니다. Redis Sentinel: 하나 이상의 Sentinel 인스턴스로 구성된 Sentinel 시스템으로, 마스터 및 슬레이브 서버를 원하는 만큼 모니터링할 수 있습니다. 모니터링되는 마스터 서버가 다운되면 마스터 서버는 자동으로 오프라인 상태가 되고 슬레이브 서버는 신규 서버로 업그레이드됩니다. 마스터 서버.
다음 예: 이전 마스터가 사용자가 설정한 오프라인 시간의 상한보다 오랫동안 오프라인 상태인 경우 Sentinel 시스템은 이전 마스터에서 장애 조치 작업을 수행합니다.
슬레이브에서 데이터 선택 최신 마스터가 새 마스터 역할을 합니다.
다른 슬레이브에 새로운 복제 지침을 보내 다른 슬레이브 서버가 새 마스터의 슬레이브가 될 수 있도록 합니다.
이전 마스터를 계속 모니터링하고, 온라인 상태가 되면 이전 마스터를 새 마스터 슬레이브로 설정합니다
이 문서는 다음 리소스 목록을 기반으로 합니다.
IP 주소 | 노드 역할 | 포트 |
---|---|---|
192.168.211.104 | Redis 마스터/센티넬 | 6379/26379 |
192.168.211.105 | Redis 슬레이브/센티넬 | 6 379/ 26379 |
192.168.211.106 | Redis 슬레이브/센티넬 | 6379/26379 |
Sentinel에는 특별한 마법이 없습니다. Sentinel이 시작되면 다양한 명령 테이블과 구성 파일을 로드하므로 본질적으로 Sentinel은 Redis 서비스입니다. 더 적은 수의 명령과 일부 특수 기능. Sentinel이 시작되면 다음 단계를 거쳐야 합니다.
Sentinel 서버 초기화
일반 Redis 코드를 Sentinel 전용 코드로 교체
Sentinel 상태 초기화
에 따르면 사용자가 제공한 Sentinel 구성 파일을 사용하여 Sentinel이 모니터링하는 마스터 서버 목록을 초기화합니다
마스터 서버에 대한 네트워크 연결 생성
마스터 서비스를 기반으로 슬레이브 서버 정보를 가져오고, 슬레이브 서버
Publish/Subscribe를 기반으로 Sentinel 정보 가져오기, Sentinel 간의 네트워크 연결 생성
Sentinel은 기본적으로 Redis 서버이므로 Sentinel을 시작하려면 Redis 서버를 시작해야 하지만 Sentinel 데이터 상태를 복원하기 위해 RDB/AOF 파일을 읽을 필요가 없습니다.
Sentinel은 더 적은 수의 Redis 명령에 사용됩니다. 대부분의 명령은 Sentinel 클라이언트에서 지원되지 않으며 Sentinel에는 시작 시 Sentinel이 Redis를 사용해야 하는 몇 가지 특수 기능이 있습니다. 서버에서 사용하는 코드는 Sentinel 전용 코드로 대체됩니다. 이 기간 동안 Sentinel은 일반 Redis 서버와 다른 명령 테이블을 로드합니다. Sentinel은 SET 및 DBSIZE와 같은 명령을 지원하지 않습니다. PING, PSUBSCRIBE, SUBSCRIBE, UNSUBSCRIBE, INFO 및 기타 명령에 대한 지원은 유지됩니다. 이러한 명령은 Sentinel의 작업을 보장합니다.
Sentinel의 고유 코드를 로드한 후 Sentinel은 Sentinel 관련 상태 정보를 저장하는 데 사용되는 sentinelState 구조를 초기화합니다. 그 중 가장 중요한 것은 마스터 사전입니다.
struct sentinelState { //当前纪元,故障转移使用 uint64_t current_epoch; // Sentinel监视的主服务器信息 // key -> 主服务器名称 // value -> 指向sentinelRedisInstance指针 dict *masters; // ... } sentinel;
Sentinel이 모니터링하는 마스터 서버 목록은 sentinelState의 masters Dictionary에 저장되며, sentinelState가 생성되면 Sentinel이 모니터링하는 마스터 서버 목록이 초기화되기 시작합니다.
마스터의 키는 메인 서비스의 이름입니다
마스터의 값은 sentinelRedisInstance
메인 서버의 이름은 sentinel.conf 구성 파일에 의해 지정됩니다. 메인 서버 이름은 redis-master (마스터 1개와 슬레이브 2개의 구성):
daemonize yes port 26379 protected-mode no dir "/usr/local/soft/redis-6.2.4/sentinel-tmp" sentinel monitor redis-master 192.168.211.104 6379 2 sentinel down-after-milliseconds redis-master 30000 sentinel failover-timeout redis-master 180000 sentinel parallel-syncs redis-master 1
sentinelRedisInstance 인스턴스는 Redis 서버의 정보를 저장합니다. (마스터 서버, 슬레이브 서버, Sentinel 정보는 모두 이 인스턴스에 저장됩니다.) .
typedef struct sentinelRedisInstance { // 标识值,标识当前实例的类型和状态。如SRI_MASTER、SRI_SLVAE、SRI_SENTINEL int flags; // 实例名称 主服务器为用户配置实例名称、从服务器和Sentinel为ip:port char *name; // 服务器运行ID char *runid; //配置纪元,故障转移使用 uint64_t config_epoch; // 实例地址 sentinelAddr *addr; // 实例判断为主观下线的时长 sentinel down-after-milliseconds redis-master 30000 mstime_t down_after_period; // 实例判断为客观下线所需支持的投票数 sentinel monitor redis-master 192.168.211.104 6379 2 int quorum; // 执行故障转移操作时,可以同时对新的主服务器进行同步的从服务器数量 sentinel parallel-syncs redis-master 1 int parallel-syncs; // 刷新故障迁移状态的最大时限 sentinel failover-timeout redis-master 180000 mstime_t failover_timeout; // ... } sentinelRedisInstance;
위의 마스터 1개와 슬레이브 2개의 구성에 따르면 다음과 같은 인스턴스 구조를 얻게 됩니다.
인스턴스 구조가 초기화된 후 Sentinel은 마스터에 대한 네트워크 연결을 생성합니다. 이 단계에서 Sentinel은 마스터의 클라이언트가 됩니다. Sentinel과 Master 사이에 명령 연결과 구독 연결이 생성됩니다.
명령 연결은 마스터-슬레이브 정보를 얻는 데 사용됩니다.
구독 연결은 Sentinel, 각 Sentinel 및 모니터링하는 마스터 간의 정보 방송에 사용됩니다. 슬레이브 서버는 _sentinel_:hello 채널을 구독합니다. (Sentinel 간에는 구독 연결이 생성되지 않으며 _sentinel_:hello 채널을 구독하여 다른 Sentinel의 초기 정보를 얻습니다.)
Sentinel은 명령 연결 완료 후 10초마다 INFO 명령이 마스터의 응답 정보를 통해 두 가지 측면의 지식을 얻을 수 있습니다.
마스터 자신의 정보
마스터 아래의 슬레이브 정보
마스터 서비스에 따라 슬레이브 서버 정보를 얻습니다. Sentinel은 슬레이브에 대한 네트워크 연결을 생성할 수 있으며 Sentinel과 Slave 간에도 구독 연결이 생성됩니다.
当Sentinel和Slave之间创建网络连接之后,Sentinel成为了Slave的客户端,Sentinel也会每隔10秒钟通过INFO指令请求Slave获取服务器信息。 到这一步Sentinel获取到了Master和Slave的相关服务器数据。这其中比较重要的信息如下:
服务器ip和port
服务器运行id run id
服务器角色role
服务器连接状态mater_link_status
Slave复制偏移量slave_repl_offset(故障转移中选举新的Master需要使用)
Slave优先级slave_priority
此时实例结构信息如下所示:
此时是不是还有疑问,Sentinel之间是怎么互相发现对方并且相互通信的,这个就和上面Sentinel与自己监视的主从之间订阅_sentinel_:hello频道有关了。 Sentinel会与自己监视的所有Master和Slave之间订阅_sentinel_:hello频道,并且Sentinel每隔2秒钟向_sentinel_:hello频道发送一条消息,消息内容如下:
PUBLISH sentinel:hello "
, , , , , , , "
其中s代码Sentinel,m代表Master;ip表示IP地址,port表示端口、runid表示运行id、epoch表示配置纪元。
多个Sentinel在配置文件中会配置相同的主服务器ip和端口信息,因此多个Sentinel均会订阅_sentinel_:hello频道,通过频道接收到的信息就可获取到其他Sentinel的ip和port,其中有如下两点需要注意:
如果获取到的runid与Sentinel自己的runid相同,说明消息是自己发布的,直接丢弃
如果不相同,则说明接收到的消息是其他Sentinel发布的,此时需要根据ip和port去更新或新增Sentinel实例数据
Sentinel之间不会创建订阅连接,它们只会创建命令连接:
此时实例结构信息如下所示:
Sentinel最主要的工作就是监视Redis服务器,当Master实例超出预设的时限后切换新的Master实例。这其中有很多细节工作,大致分为检测Master是否主观下线、检测Master是否客观下线、选举领头Sentinel、故障转移四个步骤。
Sentinel每隔1秒钟,向sentinelRedisInstance实例中的所有Master、Slave、Sentinel发送PING命令,通过其他服务器的回复来判断其是否仍然在线。
sentinel down-after-milliseconds redis-master 30000
在Sentinel的配置文件中,当Sentinel PING的实例在连续down-after-milliseconds配置的时间内返回无效命令,则当前Sentinel认为其主观下线。Sentinel的配置文件中配置的down-after-milliseconds将会对其sentinelRedisInstance实例中的所有Master、Slave、Sentinel都适应。
无效指令指的是+PONG、-LOADING、-MASTERDOWN之外的其他指令,包括无响应
如果当前Sentinel检测到Master处于主观下线状态,那么它将会修改其sentinelRedisInstance的flags为SRI_S_DOWN
当前Sentinel认为其下线只能处于主观下线状态,要想判断当前Master是否客观下线,还需要询问其他Sentinel,并且所有认为Master主观下线或者客观下线的总和需要达到quorum配置的值,当前Sentinel才会将Master标志为客观下线。
当前Sentinel向sentinelRedisInstance实例中的其他Sentinel发送如下命令:
SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <runid>
ip:被判断为主观下线的Master的IP地址
port:被判断为主观下线的Master的端口
current_epoch:当前sentinel的配置纪元
runid:当前sentinel的运行id,runid
current_epoch和runid均用于Sentinel的选举,Master下线之后,需要选举一个领头Sentinel来选举一个新的Master,current_epoch和runid在其中发挥着重要作用,这个后续讲解。
接收到命令的Sentinel,会根据命令中的参数检查主服务器是否下线,检查完成后会返回如下三个参数:
down_state:检查结果1代表已下线、0代表未下线
leader_runid:返回*代表判断是否下线,返回runid代表选举领头Sentinel
leader_epoch:当leader_runid返回runid时,配置纪元会有值,否则一直返回0
当Sentinel检测到Master处于主观下线时,询问其他Sentinel时会发送current_epoch和runid,此时current_epoch=0,runid=*
接收到命令的Sentinel返回其判断Master是否下线时down_state = 1/0,leader_runid = *,leader_epoch=0
down_state返回1,证明接收is-master-down-by-addr命令的Sentinel认为该Master也主观下线了,如果down_state返回1的数量(包括本身)大于等于quorum(配置文件中配置的值),那么Master正式被当前Sentinel标记为客观下线。 此时,Sentinel会再次发送如下指令:
SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <runid>
此时的runid将不再是0,而是Sentinel自己的运行id(runid)的值,表示当前Sentinel希望接收到is-master-down-by-addr命令的其他Sentinel将其设置为领头Sentinel。这个设置是先到先得的,Sentinel先接收到谁的设置请求,就将谁设置为领头Sentinel。 发送命令的Sentinel会根据其他Sentinel回复的结果来判断自己是否被该Sentinel设置为领头Sentinel,如果Sentinel被其他Sentinel设置为领头Sentinel的数量超过半数Sentinel(这个数量在sentinelRedisInstance的sentinel字典中可以获取),那么Sentinel会认为自己已经成为领头Sentinel,并开始后续故障转移工作(由于需要半数,且每个Sentinel只会设置一个领头Sentinel,那么只会出现一个领头Sentinel,如果没有一个达到领头Sentinel的要求,Sentinel将会重新选举直到领头Sentinel产生为止)。
故障转移将会交给领头sentinel全权负责,领头sentinel需要做如下事情:
从原先master的slave中,选择最佳的slave作为新的master
让其他slave成为新的master的slave
继续监听旧master,如果其上线,则将其设置为新的master的slave
这其中最难的一步是如果选择最佳的新Master,领头Sentinel会做如下清洗和排序工作:
判断slave是否有下线的,如果有从slave列表中移除
删除5秒内未响应sentinel的INFO命令的slave
删除与下线主服务器断线时间超过down_after_milliseconds * 10 的所有从服务器
根据slave优先级slave_priority,选择优先级最高的slave作为新master
如果优先级相同,根据slave复制偏移量slave_repl_offset,选择偏移量最大的slave作为新master
如果偏移量相同,根据slave服务器运行id run id排序,选择run id最小的slave作为新master
新的Master产生后,领头sentinel会向已下线主服务器的其他从服务器(不包括新Master)发送SLAVEOF ip port命令,使其成为新master的slave。
到这里Sentinel的的工作流程就算是结束了,如果新master下线,则循环流程即可!
Redis集群是Redis提供的分布式数据库方案,集群通过分片(sharding)进行数据共享,Redis集群主要实现了以下目标:
在1000个节点的时候仍能表现得很好并且可扩展性是线性的。
没有合并操作(多个节点不存在相同的键),这样在 Redis 的数据模型中最典型的大数据值中也能有很好的表现。
쓰기 안전, 시스템은 대부분의 노드에 연결된 클라이언트가 수행한 모든 쓰기 작업을 저장하려고 시도합니다. 그러나 Redis는 데이터가 전혀 손실되지 않는다고 보장할 수 없습니다. 비동기식 및 동기식 마스터-슬레이브 복제로 인해 데이터가 손실될 수 있습니다.
가용성, 마스터 노드를 사용할 수 없는 경우 슬레이브 노드가 마스터 노드를 대체할 수 있습니다.
Redis 클러스터 학습과 관련하여 경험이 없다면 다음 세 가지 기사(중국어 시리즈)를 읽는 것이 좋습니다. Redis 클러스터 튜토리얼
REDIS 클러스터 튜토리얼 -- Redis 중국어 정보 station -- Redis China User Group(CRUG)
Redis 클러스터 사양
REDIS Cluster-spec -- Redis Chinese Information Station -- Redis China User Group(CRUG)
Redis3 마스터 3 슬레이브 의사 클러스터 배포
CentOS 7 단일 머신에 Redis 클러스터(마스터 3개와 슬레이브 3개 의사 클러스터)를 설치하는 데는 간단한 5단계만 필요합니다_Li Zipin의 블로그 - CSDN 블로그
다음 콘텐츠는 CentOS 7의 3개 마스터와 3개 슬레이브 구조에 의존합니다. 아래 그림:
리소스 목록:
Node | IP | 슬롯 범위 |
---|---|---|
마스터[0] | 192.168.211. :6319 | 슬롯 0 - 5460 |
마스터[1] | 192.168.211.107:6329 | 슬롯 5461 - 10922 |
마스터[2] | 192.168.211.107:6339 | 슬롯 10923 - 16383 |
노예[0] | 192.168.211.107:6369 | |
노예[1] | 192.168.211.107:6349 | |
노예[2] | 192.168.211.107: 6359 |
Redis의 마스터-슬레이브 복제, Sentinel 및 클러스터링을 안내합니다.
Redis 集群没有使用一致性hash, 而是引入了 哈希槽的概念。Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,这种结构很容易添加或者删除节点。集群的每个节点负责一部分hash槽,比如上面资源清单的集群有3个节点,其槽分配如下所示:
节点 Master[0] 包含 0 到 5460 号哈希槽
节点 Master[1] 包含5461 到 10922 号哈希槽
节点 Master[2] 包含10923到 16383 号哈希槽
深入学习Redis集群之前,需要了解集群中Redis实例的内部结构。当某个Redis服务节点通过cluster_enabled配置为yes开启集群模式之后,Redis服务节点不仅会继续使用单机模式下的服务器组件,还会增加custerState、clusterNode、custerLink等结构用于存储集群模式下的特殊数据。
如下三个数据承载对象一定要认真看,尤其是结构中的注释,看完之后集群大体上怎么工作的,心里就有数了,嘿嘿嘿;
clsuterNode用于存储节点信息,比如节点的名字、IP地址、端口信息和配置纪元等等,以下代码列出部分非常重要的属性:
typedef struct clsuterNode { // 创建时间 mstime_t ctime; // 节点名字,由40位随机16进制的字符组成(与sentinel中讲的服务器运行id相同) char name[REDIS_CLUSTER_NAMELEN]; // 节点标识,可以标识节点的角色和状态 // 角色 -> 主节点或从节点 例如:REDIS_NODE_MASTER(主节点) REDIS_NODE_SLAVE(从节点) // 状态 -> 在线或下线 例如:REDIS_NODE_PFAIL(疑似下线) REDIS_NODE_FAIL(下线) int flags; // 节点配置纪元,用于故障转移,与sentinel中用法类似 // clusterState中的代表集群的配置纪元 unit64_t configEpoch; // 节点IP地址 char ip[REDIS_IP_STR_LEN]; // 节点端口 int port; // 连接节点的信息 clusterLink *link; // 一个2048字节的二进制位数组 // 位数组索引值可能为0或1 // 数组索引i位置值为0,代表节点不负责处理槽i // 数组索引i位置值为1,代表节点负责处理槽i unsigned char slots[16384/8]; // 记录当前节点处理槽的数量总和 int numslots; // 如果当前节点是从节点 // 指向当前从节点的主节点 struct clusterNode *slaveof; // 如果当前节点是主节点 // 正在复制当前主节点的从节点数量 int numslaves; // 数组——记录正在复制当前主节点的所有从节点 struct clusterNode **slaves; } clsuterNode;
上述代码中可能不太好理解的是slots[16384/8],其实可以简单的理解为一个16384大小的数组,数组索引下标处如果为1表示当前槽属于当前clusterNode处理,如果为0表示不属于当前clusterNode处理。clusterNode能够通过slots来识别,当前节点处理负责处理哪些槽。 初始clsuterNode或者未分配槽的集群中的clsuterNode的slots如下所示:
假设集群如上面我给出的资源清单,此时代表Master[0]的clusterNode的slots如下所示:
clusterLink是clsuterNode中的一个属性,用于存储连接节点所需的相关信息,比如套接字描述符、输入输出缓冲区等待,以下代码列出部分非常重要的属性:
typedef struct clusterState { // 连接创建时间 mstime_t ctime; // TCP 套接字描述符 int fd; // 输出缓冲区,需要发送给其他节点的消息缓存在这里 sds sndbuf; // 输入缓冲区,接收打其他节点的消息缓存在这里 sds rcvbuf; // 与当前clsuterNode节点代表的节点建立连接的其他节点保存在这里 struct clusterNode *node; } clusterState;
每个节点都会有一个custerState结构,这个结构中存储了当前集群的全部数据,比如集群状态、集群中的所有节点信息(主节点、从节点)等等,以下代码列出部分非常重要的属性:
typedef struct clusterState { // 当前节点指针,指向一个clusterNode clusterNode *myself; // 集群当前配置纪元,用于故障转移,与sentinel中用法类似 unit64_t currentEpoch; // 集群状态 在线/下线 int state; // 集群中处理着槽的节点数量总和 int size; // 集群节点字典,所有clusterNode包括自己 dict *node; // 集群中所有槽的指派信息 clsuterNode *slots[16384]; // 用于槽的重新分配——记录当前节点正在从其他节点导入的槽 clusterNode *importing_slots_from[16384]; // 用于槽的重新分配——记录当前节点正在迁移至其他节点的槽 clusterNode *migrating_slots_to[16384]; // ... } clusterState;
在custerState有三个结构需要认真了解的,第一个是slots数组,clusterState中的slots数组与clsuterNode中的slots数组是不一样的,在clusterNode中slots数组记录的是当前clusterNode所负责的槽,而clusterState中的slots数组记录的是整个集群的每个槽由哪个clsuterNode负责,因此集群正常工作的时候clusterState的slots数组每个索引指向负责该槽的clusterNode,集群槽未分配之前指向null。
如图展示资源清单中的集群clusterState中的slots数组与clsuterNode中的slots数组:
Redis集群中使用两个slots数组的原因是出于性能的考虑:
当我们需要获取整个集群中clusterNode分别负责什么槽时,只需要查询clusterState中的slots数组即可。如果没有clusterState的slots数组,则需要遍历所有的clusterNode结构,这样显然要慢一些
此外clusterNode中的slots数组也有存在的必要,因为集群中任意一个节点之间需要知道彼此负责的槽,此时节点之间只需要互相传输clusterNode中的slots数组结构就行。
第二个需要认真了解的结构是node字典,该结构虽然简单,但是node字典中存储了所有的clusterNode,这也是Redis集群中的单个节点获取其他主节点、从节点信息的主要位置,因此我们也需要注意一下。 第三个需要认真了解的结构是importing_slots_from[16384]数组和migrating_slots_to[16384],这两个数组在集群重新分片时需要使用,需要重点了解,后面再说吧,这里说的话顺序不太对。
Redis 클러스터에는 총 16384개의 슬롯이 있습니다. 위의 리소스 목록에 표시된 것처럼 우리는 3개의 마스터와 3개의 슬레이브가 있는 클러스터에 있습니다. 그러나 각 마스터 노드는 해당 슬롯을 담당합니다. 위의 3개의 마스터와 3개의 슬레이브의 배포 과정에서 지정했는데, 이는 Redis 클러스터 자체가 내부적으로 슬롯을 나누어 주었기 때문입니다. , 어떻게 정리해야 할까요? 다음 명령을 노드에 보내 현재 노드에 하나 이상의 슬롯을 할당할 수 있습니다.
CLUSTER ADDSLOTS
예를 들어, 마스터[0]에 슬롯 0과 1을 할당하려는 경우 마스터 [0]을 생각해야 함 노드는 다음 명령을 보낼 수 있습니다.
CLUSTER ADDSLOTS 0 1
노드에 슬롯이 할당되면 ClusterNode의 슬롯 배열이 업데이트되고 노드도 업데이트됩니다. 처리를 담당하는 슬롯(슬롯 배열) 메시지는 클러스터의 다른 노드로 전송됩니다. 메시지를 받은 후 다른 노드는 해당 클러스터 노드의 슬롯 배열과 클러스터 상태의 솔트 배열을 업데이트합니다.
이것은 실제로 비교적 간단합니다. Redis 클러스터의 노드에 CLUSTER ADDSLOTS 명령을 보내면 현재 노드는 먼저 현재 노드에 할당된 슬롯이 ClusterState의 슬롯 배열을 통해 다른 노드에 할당되지 않았는지 확인합니다. 할당된 경우 예외가 직접 발생하고 할당된 클라이언트에 오류가 반환됩니다. 현재 노드에 할당된 모든 슬롯이 다른 노드에 할당되지 않은 경우 현재 노드는 해당 슬롯을 자신에게 할당합니다. 할당에는 세 가지 주요 단계가 있습니다.
clusterState의 슬롯 배열 업데이트, 지정된 슬롯[i]을 현재 ClusterNode
clusterNode의 슬롯 배열 업데이트, 지정된 슬롯[i]의 값 업데이트 ] ~ 1
클러스터의 다른 노드에 메시지를 보내고, ClusterNode의 슬롯 배열을 다른 노드에 보냅니다. 메시지를 받은 후 다른 노드도 해당 ClusterState의 슬롯 배열과 ClusterNode
이 문제를 이해하기 전에 먼저 한 가지 사항을 알아야 합니다. Redis 클러스터는 현재 키가 속한 슬롯을 어떻게 계산합니까? 공식 웹사이트에 따르면 Redis는 실제로 일관된 해시 알고리즘을 사용하지 않습니다. 대신 요청된 각 키를 CRC16에서 확인한 다음 모듈로 16384를 사용하여 키를 배치할 슬롯을 결정합니다.
이때 클라이언트가 노드에 요청을 보내기 위해 접속하면, 현재 명령을 받은 노드는 먼저 현재 키가 있는 슬롯 i를 계산하게 됩니다. 알고리즘을 통해 계산 후 현재 노드는 ClusterState의 슬롯 i가 자신을 담당하는지 여부를 결정합니다. 만약 자신이 담당하고 있다면 현재 노드는 클라이언트의 요청에 응답합니다. 현재 노드의 경우 다음 단계를 거치게 됩니다.HASH_SLOT = CRC16(key) mod 16384
CLUSTER 用于槽分配的指令主要有如上这些,ADDSLOTS 和DELSLOTS主要用于槽的快速指派和快速删除,通常我们在集群刚刚建立的时候进行快速分配的时候才使用。CLUSTER SETSLOT slot NODE node也用于直接给指定的节点指派槽。如果集群已经建立我们通常使用最后两个来重分配,其代表的含义如下所示:
当一个槽被设置为 MIGRATING,原来持有该哈希槽的节点仍会接受所有跟这个哈希槽有关的请求,但只有当查询的键还存在原节点时,原节点会处理该请求,否则这个查询会通过一个 -ASK 重定向(-ASK redirection)转发到迁移的目标节点。
当一个槽被设置为 IMPORTING,只有在接受到 ASKING 命令之后节点才会接受所有查询这个哈希槽的请求。如果客户端一直没有发送 ASKING 命令,那么查询都会通过 -MOVED 重定向错误转发到真正处理这个哈希槽的节点那里。
上面这两句话是不是感觉不太看的懂,这是官方的描述,不太懂的话我来给你通俗的描述,整个流程大致如下步骤:
redis-trib(集群管理软件redis-trib会负责Redis集群的槽分配工作),向目标节点(槽导入节点)发送CLUSTER SETSLOT slot IMPORTING node命令,目标节点会做好从源节点(槽导出节点)导入槽的准备工作。
redis-trib随即向源节点发送CLUSTER SETSLOT slot MIGRATING node命令,源节点会做好槽导出准备工作
redis-trib随即向源节点发送CLUSTER GETKEYSINSLOT slot count命令,源节点接收命令后会返回属于槽slot的键,最多返回count个键
redis-trib会根据源节点返回的键向源节点依次发送MIGRATE ip port key 0 timeout命令,如果key在源节点中,将会迁移至目标节点。
迁移完成之后,redis-trib会向集群中的某个节点发送CLUSTER SETSLOT slot NODE node命令,节点接收到命令后会更新clusterNode和clusterState结构,然后节点通过消息传播槽的指派信息,至此集群槽迁移工作完成,且集群中的其他节点也更新了新的槽分配信息。
优秀的你总会想到这种并发情况,牛皮呀!大佬们!
这个问题官方也考虑了,还记得我们在聊clusterState结构的时候么?importing_slots_from和migrating_slots_to就是用来处理这个问题的。
typedef struct clusterState { // ... // 用于槽的重新分配——记录当前节点正在从其他节点导入的槽 clusterNode *importing_slots_from[16384]; // 用于槽的重新分配——记录当前节点正在迁移至其他节点的槽 clusterNode *migrating_slots_to[16384]; // ... } clusterState;
当节点正在导出某个槽,则会在clusterState中的migrating_slots_to数组对应的下标处设置其指向对应的clusterNode,这个clusterNode会指向导入的节点。
当节点正在导入某个槽,则会在clusterState中的importing_slots_from数组对应的下标处设置其指向对应的clusterNode,这个clusterNode会指向导出的节点。
有了上述两个相互数组,就能判断当前槽是否在迁移了,而且从哪里迁移来,要迁移到哪里去?搞笑不就是这么简单……
此时,回到问题中,如果客户端请求的key刚好属于正在迁移的槽。那么接收到命令的节点首先会尝试在自己的数据库中查找键key,如果这个槽还没迁移完成,且当前key刚好也还没迁移完成,那就直接响应客户端的请求就行。如果该key已经不在了,此时节点会去查询migrating_slots_to数组对应的索引槽,如果索引处的值不为null,而是指向了某个clusterNode结构,那说明这个key已经被迁移到这个clusterNode了。这个时候节点不会继续在处理指令,而是返回ASKING命令,这个命令也会携带导入槽clusterNode对应的ip和port。客户端在接收到ASKING命令之后就需要将请求转向正确的节点了,不过这里有一点需要注意的地方**(因此我放个表情包在这里,方便读者注意)。**
앞서 언급했듯이 노드는 현재 슬롯이 자체 처리에 속하지 않는다는 것을 발견하면 MOVED 명령을 반환합니다. 그러면 마이그레이션 중인 슬롯을 어떻게 처리합니까? 이것이 Redis 클러스터의 목적입니다. 노드가 슬롯이 마이그레이션되는 것을 발견하면 ASKING 명령을 클라이언트에 반환합니다. 클라이언트는 슬롯이 마이그레이션되는 클러스터 노드의 노드 IP와 포트가 포함된 ASKING 명령을 수신합니다. 그런 다음 클라이언트는 먼저 마이그레이션하는 ClusterNode에 ASKING 명령을 보냅니다. 이 명령의 목적은 이 슬롯이 사용자에게 마이그레이션되었기 때문에 이 요청을 처리하기 위해 예외를 만들어야 함을 현재 노드에 알리는 것입니다. 나를 직접 거부합니다(따라서 Redis가 ASKING 명령을 수신하지 않으면 노드의 ClusterState를 직접 쿼리하고 마이그레이션 중인 슬롯은 ClusterState로 업데이트되지 않았으므로 MOVED만 직접 반환할 수 있으므로 계속 반복됩니다. 여러 번...), 수신됨 ASKING 명령이 있는 노드는 이 요청을 강제로 한 번만 실행합니다(한 번만, 다음번에는 미리 ASKING 명령을 다시 보내야 합니다).
Redis 클러스터 실패는 실제로 마스터 노드가 다운되거나 지정된 최대 시간 내에 응답하지 않고 새로운 마스터 노드가 선택되는 경우와 유사합니다. 슬레이브 노드. 물론 전제는 Redis 클러스터의 각 마스터 노드에 대해 슬레이브 노드를 미리 설정해 두었다는 것입니다. 그렇지 않으면 쓸모가 없습니다... 일반적인 단계는 다음과 같습니다.
정상적으로 작동하는 클러스터에서 각 노드는 정기적으로 다른 노드에 PING 명령을 보냅니다. 명령을 받은 노드가 지정된 시간 내에 PONG 메시지를 반환하지 않으면 현재 노드가 PING 명령을 보냅니다. 명령을 수신하는 노드에 대한 PING 명령은 클러스터 노드의 플래그가 REDIS_NODE_PFAIL로 설정되어 있습니다. PFAIL은 오프라인이 아니지만 오프라인인 것으로 의심됩니다.
클러스터 노드는 클러스터 내 각 노드의 상태 정보 메시지를 보내 다른 노드에 알립니다.
슬롯 처리를 담당하는 클러스터의 마스터 노드 중 절반 이상이 특정 마스터 노드를 의심되는 것으로 설정한 경우 오프라인이면 노드는 오프라인으로 표시되고 노드는 명령을 수신하는 노드의 클러스터 노드의 플래그를 REDIS_NODE_FAIL로 설정합니다. FAIL은 오프라인임을 나타냅니다.
클러스터 노드는 메시지를 보내 다른 노드에 알립니다. 클러스터 오프라인 노드의 각 노드 상태 정보 이때 오프라인 노드의 슬레이브 노드는 자신의 마스터 노드가 오프라인으로 표시된 것을 발견하므로 이제 오프라인 마스터 노드의 슬레이브 노드가 앞으로 나아갈 시간입니다. 슬레이브 노드를 최신 마스터 노드로 생성하고 선택한 노드를 실행하여 SLAVEOF 누구도 새 마스터 노드가 되지 않도록 지정합니다
새 마스터 노드는 원래 마스터 노드의 슬롯 할당을 취소하고 이러한 슬롯을 수정합니다. 즉, ClusterNode 구조와 ClusterState 구조를 수정합니다
새 마스터 노드가 PONG 명령을 클러스터에 브로드캐스트합니다. 다른 노드는 새 마스터 노드가 생성되었음을 알고 ClusterNode 구조와 ClusterState 구조를 업데이트합니다.
새 마스터 노드 원래 마스터 노드의 나머지 슬레이브 노드에 새 SLAVEOF 명령이 전송되면 이를 자체 슬레이브 노드로 만듭니다
마지막 새 마스터 노드가 마스터 노드의 응답 작업을 담당하게 됩니다. 원래 마스터 노드의 슬롯
여기에 많은 내용을 썼습니다. Fuzzy, 자세한 내용이 필요하면 다음 문서를 읽어야 합니다.
또는 Huang Jianhong이 쓴 "Redis Design and Implementing" 책을 읽어보셔도 좋습니다. 많은 콘텐츠. 더 많은 프로그래밍 관련 지식을 보려면프로그래밍 비디오
를 방문하세요! !
위 내용은 Redis의 마스터-슬레이브 복제, Sentinel 및 클러스터링을 안내합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!