(十)redis高可用之主从复制replication+哨兵sentinal

单机的redis几乎不太可能说QPS超过10万+,除非机器性能特别好。那如何支撑20w+QPS呢--->答案就是读写分离

那就需要进行数据同步。redis支持主从同步和从从同步

(一)redis同步(replication)核心机制

  1. redis采用异步方式复制数据到slave节点,不过redis 2.8开始,slave node会周期性地确认自己每次复制的数据量
  2. 一个master node是可以配置多个slave node的
  3. slave node也可以连接其他的slave node
  4. slave node做复制的时候,是不会block master node的正常工作的
  5. slave node在做复制的时候,也不会block对自己的查询操作,它会用旧的数据集来提供服务; 但是复制完成的时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了
  6. slave node主要用来进行横向扩容,做读写分离,扩容的slave node可以提高读的吞吐量

 

(二)同步方式:增量同步/快照同步

增量同步

主节点会将对自己状态产生修改的指令记录在本地的内存buffer中,然后异步将buffer中的指令同步给从节点。从节点一边执行同步的指令来达到和主节点状态一致,一边向主节点反馈自己同步到了哪里(偏移量)。

因为内存的buffer有限,redis主节点不能将所有指令都记录在buffer中,redis的复制内存buffer是一个定长的环形数组,如果内存满了会进行覆盖。这时候就需要更加复杂的同步机制--快照同步。

(十)redis高可用之主从复制replication+哨兵sentinal_第1张图片

快照同步

快照同步是一个非常耗费资源的操作,首先在主库进行一次bgsave将当前内存的数据全部快照到磁盘文件中,然后将快照的文件的内容全部传送到从节点,从节点立即清空当前内存再执行一次全量加载加载完成后通知主节点进行增量同步。

问题:如果快照同步时间过长或者复制buffer太小,导致同步期间的增量指令在辅助buffer中内覆盖,就会导致快照同步完成后无法进行增量复制,再次发起快照复制,极有可能陷入快照同步的死循环

解决方案:配置一个合适的复制buffer,避免快照复制的死循环

详细步骤:

  1. master执行bgsave,在本地生成一份rdb快照文件
  2. master node将rdb快照文件发送给salve node,如果rdb复制时间超过60秒(repl-timeout),那么slave node就会认为复制失败,可适当调节大这个参数
  3. 对于千兆网卡的机器,一般每秒传输100MB,6G文件,很可能超过60s
  4. master node在生成rdb时,会将所有新的写命令缓存在内存中,在salve node保存了rdb之后,再将新的写命令复制给salve node
  5. client-output-buffer-limit slave 256MB 64MB 60,如果在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,那么停止复制,复制失败
  6. slave node接收到rdb之后,清空自己的旧数据,然后重新加载rdb到自己的内存中,同时基于旧的数据版本对外提供服务
  7. 如果slave node开启了AOF,那么会立即执行bgrewriteAOF,重写AOF
  8. 如果复制的数据量在4G~6G之间,那么很可能全量复制时间消耗到1分半到2分钟

 

(三)主从架构核心原理

1、主从架构的核心原理

当启动一个slave node的时候,它会发送一个PSYNC命令给master node

如果这是slave node重新连接master node,那么master node仅仅会复制给slave部分缺少的数据; 否则如果是slave node第一次连接master node,那么会触发一次full resynchronization

开始full resynchronization的时候,master会启动一个后台线程,开始生成一份RDB快照文件,同时还会将从客户端收到的所有写命令缓存在内存中。RDB文件生成完毕之后,master会将这个RDB发送给slave,slave会先写入本地磁盘,然后再从本地磁盘加载到内存中。然后master会将内存中缓存的写命令发送给slave,slave也会同步这些数据。

slave node如果跟master node有网络故障,断开了连接,会自动重连。master如果发现有多个slave node都来重新连接,仅仅会启动一个rdb save操作,用一份数据服务所有slave node。

2、主从复制的断点续传

从redis 2.8开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份

master node会在内存中常见一个backlog,master和slave都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。如果master和slave网络连接断掉了,slave会让master从上次的replica offset开始继续复制

但是如果没有找到对应的offset,那么就会执行一次resynchronization

3、无磁盘化复制

master在内存中直接创建rdb,然后发送给slave,不会在自己本地落地磁盘了

repl-diskless-sync

repl-diskless-sync-delay,等待一定时长再开始复制,因为要等更多slave重新连接过来

4、过期key处理

slave不会过期key,只会等待master过期key。如果master过期了一个key,或者通过LRU淘汰了一个key,那么会模拟一条del命令发送给slave。

 

(四)哨兵sentinal

1)哨兵的介绍

哨兵是redis集群架构中非常重要的一个组件,主要功能如下

  1. 集群监控,负责监控redis master和slave进程是否正常工作
  2. 消息通知,如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
  3. 故障转移,如果master node挂掉了,会自动转移到slave node上
  4. 配置中心,如果故障转移发生了,通知client客户端新的master地址

哨兵本身也是分布式的,作为一个哨兵集群去运行,互相协同工作

  1. 故障转移时,判断一个master node是宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举的问题
  2. 即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就很坑爹了

2)哨兵的核心知识

  1. 哨兵至少需要3个实例,来保证自己的健壮性
  2. 哨兵 + redis主从的部署架构,是不会保证数据零丢失的,只能保证redis集群的高可用性
  3. 对于哨兵 + redis主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练

3)数据丢失的情况

1、两种数据丢失的情况(其实都是主备切换的过程,可能会导致数据丢失)

(1)异步复制导致的数据丢失

因为master -> slave的复制是异步的,所以可能有部分数据还没复制到slave,master就宕机了,此时这些部分数据就丢失了

(2)脑裂导致的数据丢失

脑裂,也就是说,某个master所在机器突然脱离了正常的网络,跟其他slave机器不能连接,但是实际上master还运行着

此时哨兵可能就会认为master宕机了,然后开启选举,将其他slave切换成了master

这个时候,集群里就会有两个master,也就是所谓的脑裂

此时虽然某个slave被切换成了master,但是可能client还没来得及切换到新的master,还继续写向旧master的数据可能也丢失了

因此旧master再次恢复的时候,会被作为一个slave挂到新的master上去,自己的数据会清空,重新从新的master复制数据

 

2、解决异步复制和脑裂导致的数据丢失

min-slaves-to-write 1

min-slaves-max-lag 10

要求至少有1个slave,数据复制和同步的延迟不能超过10秒

如果说一旦所有的slave,数据复制和同步的延迟都超过了10秒钟,那么这个时候,master就不会再接收任何请求了

上面两个配置可以减少异步复制和脑裂导致的数据丢失

(1)减少异步复制的数据丢失

有了min-slaves-max-lag这个配置,就可以确保说,一旦slave复制数据和ack延时太长,就认为可能master宕机后损失的数据太多了,那么就拒绝写请求,这样可以把master宕机时由于部分数据未同步到slave导致的数据丢失降低的可控范围内

(2)减少脑裂的数据丢失

如果一个master出现了脑裂,跟其他slave丢了连接,那么上面两个配置可以确保说,如果不能继续给指定数量的slave发送数据,而且slave超过10秒没有给自己ack消息,那么就直接拒绝客户端的写请求

这样脑裂后的旧master就不会接受client的新数据,也就避免了数据丢失

上面的配置就确保了,如果跟任何一个slave丢了连接,在10秒后发现没有slave给自己ack,那么就拒绝新的写请求

因此在脑裂场景下,最多就丢失10秒的数据

4)哨兵核心底层原理

1、sdown和odown转换机制

sdown和odown两种失败状态

  • sdown是主观宕机:就一个哨兵如果自己觉得一个master宕机了,那么就是主观宕机
  • odown是客观宕机:如果quorum数量的哨兵都觉得一个master宕机了,那么就是客观宕机

sdown达成的条件很简单,如果一个哨兵ping一个master,超过了is-master-down-after-milliseconds指定的毫秒数之后,就主观认为master宕机

sdown到odown转换的条件很简单,如果一个哨兵在指定时间内,收到了quorum(法定数量)指定数量的其他哨兵也认为那个master是sdown了,那么就认为是odown了,客观认为master宕机

2、哨兵集群的自动发现机制

哨兵互相之间的发现,是通过redis的pub/sub系统实现的,每个哨兵都会往__sentinel__:hello这个channel里发送一个消息,这时候所有其他哨兵都可以消费到这个消息,并感知到其他的哨兵的存在

每隔两秒钟,每个哨兵都会往自己监控的某个master+slaves对应的__sentinel__:hello channel里发送一个消息,内容是自己的host、ip和runid还有对这个master的监控配置

每个哨兵也会去监听自己监控的每个master+slaves对应的__sentinel__:hello channel,然后去感知到同样在监听这个master+slaves的其他哨兵的存在

每个哨兵还会跟其他哨兵交换对master的监控配置,互相进行监控配置的同步

3、slave配置的自动纠正

哨兵会负责自动纠正slave的一些配置,比如slave如果要成为潜在的master候选人,哨兵会确保slave在复制现有master的数据; 如果slave连接到了一个错误的master上,比如故障转移之后,那么哨兵会确保它们连接到正确的master上

4、slave->master选举算法

如果一个master被认为odown了,而且majority(大多数)哨兵都允许了主备切换,那么某个哨兵就会执行主备切换操作,此时首先要选举一个slave来

会考虑slave的一些信息

(1)跟master断开连接的时长

(2)slave优先级

(3)复制offset

(4)run id

如果一个slave跟master断开连接已经超过了down-after-milliseconds的10倍,外加master宕机的时长,那么slave就被认为不适合选举为master

(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state

接下来会对slave进行排序

(1)按照slave优先级进行排序,slave priority越低,优先级就越高

(2)如果slave priority相同,那么看replica offset,哪个slave复制了越多的数据,offset越靠后,优先级就越高

(3)如果上面两个条件都相同,那么选择一个run id比较小的那个slave

5、quorum和majority

每次一个哨兵要做主备切换,首先需要quorum数量的哨兵认为odown,然后选举出一个哨兵来做切换,这个哨兵还得得到majority哨兵的授权,才能正式执行切换

如果quorum < majority,比如5个哨兵,majority就是3,quorum设置为2,那么就3个哨兵授权就可以执行切换

但是如果quorum >= majority,那么必须quorum数量的哨兵都授权,比如5个哨兵,quorum是5,那么必须5个哨兵都同意授权,才能执行切换

6、configuration epoch

哨兵会对一套redis master+slave进行监控,有相应的监控的配置

执行切换的那个哨兵,会从要切换到的新master(salve->master)那里得到一个configuration epoch,这就是一个version号,每次切换的version号都必须是唯一的

如果第一个选举出的哨兵切换失败了,那么其他哨兵,会等待failover-timeout时间,然后接替继续执行切换,此时会重新获取一个新的configuration epoch,作为新的version号

7、configuraiton传播

哨兵完成切换之后,会在自己本地更新生成最新的master配置,然后同步给其他的哨兵,就是通过之前说的pub/sub消息机制

这里之前的version号就很重要了,因为各种消息都是通过一个channel去发布和监听的,所以一个哨兵完成一次新的切换之后,新的master配置是跟着新的version号的

其他的哨兵都是根据版本号的大小来更新自己的master配置的

 

 

 

 

你可能感兴趣的:(redis深度历险,redis,java,缓存)