redis集群方案
一、主从复制
1、主从怎么保持数据一致
Redis 的主从库同步有三种模式:全量复制、基于长连接的命令传播,以及增量复制。
1.第一次主从库同步
它们相互之间就可以通过 replicaof命令形成主库和从库的关系,之后会按照如下图所示三个阶段完成数据的第一次同步

FULLRESYNC 响应表示第一次复制采用的全量复制,也就是说,主库会把当前所有的数据都复制给从库。(以RDB的形式)
在主库将数据同步给从库的过程中,主库不会被阻塞,仍然可以正常接收请求。但是,这些请求中的写操作并没有记录到刚刚生成的 RDB 文件中。为了保证主从库的数据一致性,主库会在内存中用专门的 replication buffer,记录 RDB 文件生成后收到的所有写操作。最后,当主库完成 RDB 文件发送后,就会把此时 replication buffer 中的修改操作发给从库,从库再重新执行这些操作。这样一来,主从库就实现同步了
主从级联模式分担全量复制时的主库压力
通过“主 - 从 - 从”模式将主库生成 RDB 和传输 RDB 的压力,以级联的方式分散到从库上
2.基于长连接的命令传播
从库完成了全量复制,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,可以避免频繁建立连接的开销。
3.增量复制
如果第二步的网络断开了,主从库会采用增量复制的方式继续同步。
repl_backlog_buffer(复制积压缓冲区) 是一个环形缓冲区,主库会记录自己写到的位置,从库则会记录自己已经读到的位置(每个从库都会记录,只要有从库存在,repl_backlog_buffer就会存在,只不过在网络断开的时候才会起作用。)
刚开始,主库和从库的写读位置在一起,这算是它们的起始位置。随着主库不断接收新的写操作,它在缓冲区中的写位置会逐步偏离起始位置,对主库来说,对应的偏移量就是 master_repl_offset。主库接收的新写操作越多,这个值就会越大。同样,从库在复制完写操作命令后,它在缓冲区中的读位置也开始逐步偏移刚才的起始位置,此时,从库已复制的偏移量 slave_repl_offset 也在不断增加。正常情况下,这两个偏移量基本相等。
主从库的连接恢复之后,从库首先会给主库发送 psync 命令,并把自己当前的 slave_repl_offset 发给主库,主库会判断自己的 master_repl_offset 和 slave_repl_offset 之间的差距。
如果从库断开时间太久,repl_backlog_buffer环形缓冲区被主库的写命令覆盖了,那么从库连上主库后只能乖乖地进行一次全量同步,所以repl_backlog_buffer配置尽量大一些,可以降低主从断开后全量同步的概率。而在repl_backlog_buffer中找主从差异的数据后,在通过buffer发给从库。
再延伸一下, replication buffer是有限制的,如果主从在传播命令时,因为某些原因从库处理得非常慢,那么主库上的这个buffer就会持续增长,消耗大量的内存资源,甚至OOM。所以Redis提供了client-output-buffer-limit参数限制这个buffer的大小,如果超过限制,主库会强制断开这个client的连接,也就是说从库处理慢导致主库内存buffer的积压达到限制后,主库会强制断开从库的连接,此时主从复制会中断,中断后如果从库再次发起复制请求,那么此时可能会导致恶性循环,引发复制风暴,这种情况需要格外注意。
二、哨兵机制
Redis 的哨兵机制自动完成了以下三大功能,从而实现了主从库的自动切换
1.监控主库运行状态,并判断主库是否客观下线;(为了降低误判,哨兵机制通常采用多实例的方式进行部署,多个哨兵实例通过“少数服从多数”的原则,来判断主库是否客观下线)
2.在主库客观下线后,选取新主库;(先按照筛选条件(在线状态,以及判断之前的网络连接状态),把不符合条件的从库去掉。然后按照一定的规则,给剩下的从库逐个打分,将得分最高的从库选为新主库)
3.选出新主库后,通知从库和客户端。
哨兵挂了,主从库还能切换吗?
1.基于 pub/sub 机制的哨兵集群组成过程;
哨兵只要和主库建立起了连接,就可以在主库上发布消息了,比如说发布它自己的连接信息(IP 和端口)。同时,它也可以从主库上订阅消息,获得其他哨兵发布的连接信息。

2.基于 INFO 命令的从库列表,这可以帮助哨兵和从库建立连接;
主库接受到INFO命令后,就会把从库列表返回给哨兵。接着,哨兵就可以根据从库列表中的连接信息,和每个从库建立连接,并在这个连接上持续地对从库进行监控

3.基于哨兵自身的 pub/sub 功能,这实现了客户端和哨兵之间的事件通知。
客户端可以从哨兵订阅消息。哨兵提供的消息订阅频道有很多,不同频道包含了主从库切换过程中的不同关键事件。有了这些事件通知,客户端不仅可以在主从切换后得到新主库的连接信息,还可以监控到主从库切换过程中发生的各个重要事件。这样,客户端就可以知道主从切换进行到哪一步了,有助于了解切换进度。

三、redis cluster(分片集群)
在分片集群中,数据需要分布在不同实例上,Redis Cluster 方案采用哈希槽( Slot),来处理数据和实例之间的映射关系。在 Redis Cluster 方案中,一个切片集群共有 16384 个哈希槽,这些哈希槽类似于数据分区,每个键值对都会根据它的 key,被映射到一个哈希槽中。下面例子比较清晰

①:请求路由
集群刚刚创建的时候,每个实例只知道自己被分配了哪些哈希槽,是不知道其他实例拥有的哈希槽信息的。但每Redis 实例会把自己的哈希槽信息发给和它相连接的其它实例(所有实例都是互相连接的),来完成哈希槽分配信息的扩散,所以当实例之间相互连接后,每个实例就有所有哈希槽的映射关系了。客户端和集群实例建立连接后,实例就会把哈希槽的分配信息发给客户端。客户端收到哈希槽信息后,会把哈希槽信息缓存在本地。当客户端请求键值对时,会先计算键所对应的哈希槽,然后就可以给相应的实例发送请求了。
具体的映射过程分为两大步:第一根据键值对的 key,按照CRC16 算法计算一个 16 bit 的值;然后,再用这个 16bit 值对 16384 取模,得到 0~16383 范围内的模数,每个模数代表一个相应编号的哈希槽,第二通过客户端本地缓存的哈希槽信息,找到对应的实例,发送请求
②:数据迁移
当集群节点不足以支撑业务需求时,就需要扩容节点,扩容就意味着节点之间的数据需要做迁移,而迁移过程中是否会影响到业务,这也是判定一个集群方案是否成熟的标准
Redis Cluster 方案提供了一种重定向机制,所谓的“重定向”,就是指,当客户端把一个键值对的操作请求发给一个实例时,如果这个实例上并没有这个键值对映射的哈希槽,那么,这个实例就会给客户端返回下面的 MOVED 命令响应结果,GET hello:key(error) MOVED 13320 172.16.19.5:6379 这个结果中就包含了新实例的访问地址,同时还会更新本地缓存,具体流程如下图所示,solt2迁移到实例3上

需要注意的是,在上图中,当客户端给实例 2 发送命令时,Slot 2 中的数据已经全部迁移到了实例 3。在实际应用时,如果 Slot 2 中的数据比较多,就可能会出现一种情况:客户端向实例 2 发送请求,但此时,Slot 2 中的数据只有一部分迁移到了实例 3,还有部分数据没有迁移。在这种迁移部分完成的情况下,客户端就会收到一条 ASK 报错信息,GET hello:key(error) ASK 13320 172.16.19.5:6379 如下所示

和 MOVED 命令不同,ASK 命令并不会更新客户端缓存的哈希槽分配信息
Redis 主从库同步时可能出现的 3 个坑

参考资料:https://segmentfault.com/a/1190000022028642

浙公网安备 33010602011771号