Redis提供了三种集群方案对应不同场景, 分别是主从复制, 哨兵, 切片
主从复制就是将复制当前的服务器实例, 将一台Redis服务器的数据同步到多台Redis服务器上, 当主服务器挂掉后, 可以立刻将从服务器替换上去(哨兵机制)
, 保证一直有Redis服务器可以提供服务
主从复制应该关心那些方面?
主从服务器的数据一致性
主从服务器之间的连接维护
每一个从服务器都需要先同主服务器进行同步, 复制主服务器上的数据后才能投入运行
从服务器会建立与主服务器之间的连接, 主服务器会通过这个连接将数据同步给从服务器
主服务器会将本服务器上的数据打包成RDB文件发送给客户端, 客户端通过RDB文件来复制主服务器上的所有数据
主从服务器在同步RDB文件的时候, 客户端写对主服务器执行写操作怎么办?从服务器通过RDB恢复的数据不就和主服务器不一致了
主服务器会将同步期间收到的写入命令缓存起来, 当从服务器使用RDB文件复制好数据后, 主服务器会将缓冲区中的数据发送给从服务器
同步完成后, 主从数据库会达到一致状态, 但是对主服务器后续执行的写命令会导致主服务器的数据状态更新, 所以从服务器也需要跟着更新来保证主从一致性
这个过程是通过命令传播来实现的
主服务器会将执行的写命令发送给从服务区, 从服务器接收命令后在本机执行来达到和主服务器一致的数据状态
这里的写命令传输是通过主从之间一开始建立的长连接来传输的, 这个连接是一个长连接
命令传播过程中主从服务器连接断开重连会发生什么
命令传播过程中主从服务器连接中断重连后, 从服务器与主服务器肯定是进入了一个数据不同步的情况, 在这种情况下如果主服务器不知道从服务器是从哪里开始出现数据不一致
的情况的话, 就只能发送命令让从服务器清空自己, 然后重新开始同步
2.8以前的Redis就是因为主服务器无法知道从服务器的命令传播进度, 所以每次断线重连都需要重新同步, 效率非常低
2.8版本以后, Redis推出了部分重同步方案, 解决了从服务器重连之后要重新同步的问题
部分重同步方案是怎么实现的?
部分重同步方案基于三部分实现
复制偏移量
复制缓冲区
主从服务器的标识ID
每条发送给从服务器的命令都会被缓存到复制缓冲区, 当从服务器执行完成命令之后, 会将内部维护的复制偏移量
递增
当掉线重连之后, 从服务器会发送从服务器的复制偏移量给主服务器, 如果复制偏移量在主服务器维护的复制缓冲区中, 就可以从复制偏移量的下一位置进行命令传播
整个复制缓冲区是固定大小的, 当满了之后后续写入的命令就会将前面的命令覆盖, 防止复制缓冲区过大维护困难
主服务器如何知道当前连接上的从服务器是可以进行部分重同步?
这点是通过主从服务器ID标识来实现的
从服务器会在第一次同步的时候保存主服务器的运行ID, 如果重连前后主服务器的ID相同, 从服务器就会发送带有偏移量的PSYNC
命令, 来向主服务器询问是否可以进行部分重同步
主从复制中键值对过期处理
主从复制模型中从服务器是不会提供写服务的, 所有的数据写入操作都是由主服务器负责的
当从服务器中的键值对过期, 从节点不会删除它, 而是等待主节点发送del命令来让从节点执行删除操作
为什么要让主节点来负责主从集群中的写操作?
因为如果主从节点都能同时负责写操作的话, 必定会引入写冲突问题, 这个就需要上锁来避免冲突
这样的性能很低, 所以Redis没有使用
什么时候会出现主从服务器数据不一致
情况一 : 主从复制延迟
因为主从复制是一异步过程, 主服务器执行完成写操作后就立刻返回了, 不会等待从服务器接收执行完命令再返回, 那样做太低效了
解决思路 : 尽可能保证主从服务器之间的网络通畅, 尽可能地减少主从复制不一致的时间段
情况二 : 主从切换数据丢失问题
命令传播这个异步过程中, 如果主服务器中途挂掉了, 那么就会重新推选一个新的主服务器上来
但是选出新的主服务器会清空现有的所有从服务器的数据, 并重新进行同步来保证数据一致性
此时原主服务器上的数据就丢失了
解决思路 : 设置主从偏移量的阈值, 当偏移量差值过大的时候就禁止主节点写数据, 让从节点尽快复制好主服务器上的数据
主从节点之间是使用长连接来维护的
从服务器会每秒发送一个心跳包给主服务器, 心跳包中会包含当前从服务器的复制偏移量, 用于检测主从服务器之间的网络状况
如果服务器收到的从服务器偏移量低于阈值, 就会禁止写操作
同时主服务器会根据从服务器的偏移量来判断命令传播过程中是否会有命令丢失, 如果丢失就进行命令重传
主服务器会每10s定期发送PING命令给从服务器, 从服务器需要及时回复PONG命令来告知主服务器存活
哨兵就是负责站岗监视的士兵, 在集群中发挥的作用就是监控集群中服务器的状况, 及时发现服务器掉线并进行主从切换, 保证整个系统会有一个主服务器和零到多个从服务器提供服务
重点关注以下问题
哨兵如何判断服务器掉线?掉线后如何处理
哨兵是如何组织的
服务器掉线检测分为两部分 : 主观下线和客观下线
如何判断主观下线
哨兵服务器会每秒发送心跳包给所有创建了命令的实例, 如果在指定的时间内没有收到有效的回复的话就会标识该服务器进入主观下线状态
如何判断客观下线状态
在一个哨兵服务器将一个主服务器判断为主观下线后, 会向同样检测的其他哨兵进行询问, 只要接收到足够数量的下线判断哨兵服务器就会将服务器判断为客观下线, 然后执行故障转移操作
为什么要设定主观和客观下线状态
当哨兵节点观测到主观下线的时候, 可能是因为 网络原因 | 其他原因 导致主节点没有在规定时间内响应哨兵的PING命令
为了避免因为哨兵误判导致错误地进行主从切换, 最好是部署 3 台以上的哨兵节点构成哨兵集群, 通过多个节点一起判断来避免因为当个哨兵自身的原因导致误判
如果掉线的是主服务器, 就需要进行故障转移, 选择一个从服务器来进行主从切换
主从切换分为下面几个流程
选举领头哨兵
领头哨兵选取从服务器
进行主从切换
重点就是选举领头哨兵这个过程了
如何选取领头哨兵
判断节点客观下线的哨兵就是一个候选的领头哨兵, 此时领头哨兵会向其他哨兵发送命令传达自己想要进行主从切换, 自己要当老大, 让其他哨兵投自己一票
整个哨兵集群中的每个哨兵都会有宝贵一票, 只要候选哨兵收获到半数以上的赞成票并且票数大于哨兵配置文件中的基本票数就会被选举为领头哨兵
为什么要选举领头哨兵
防止同一时间两个哨兵节点同时判断一个主节点客观下线后同时进行故障转移
如何选取新的主服务器
新的领头哨兵会在已下线的主服务器下属的所有从服务器选一个状态良好数据完整的作为新从服务器
Redis会有三种排序来决定新的主服务器
a. 先对所有节点进行优先级排序
b. 当最高优先级的节点有多个的情况下, 对节点的复制量进行进行排序
c. 根据ID判断, ID小的节点作为新的主节点
一个个判断知道判断出某个服务器优先级高就选举为新的主服务器
故障转移
哨兵服务器会发送命令修改从服务器的主服务器, 让其他的从服务器认准新的大哥
同时会给下线的旧主服务器打上标识, 当旧主服务器重新连接上来的时候, 会发送命令让旧主服务器去当小弟去同步新主服务器上的数据
哨兵服务器会订阅主服务器的__sentinel__:hello
频道, 当有哨兵服务器连接上主服务器之后, 就会在__sentinel__:hello
频道中发送哨兵本身的数据信息供其他哨兵发现
其他哨兵发现有新的哨兵加入了集群后, 就会建立和新哨兵的命令连接, 用于哨兵之间的命令交互
之所以哨兵服务器不使用主服务器的__sentinel__:hello
频道来进行交互的原因是通信会受到主服务器之间的网络影响
当主服务器掉线了, 相当于整个哨兵系统失联了
哨兵服务器是在启动的时候就配置好了主服务器信息, 在启动的时候就会像Redis客户端一样建立和主服务器的连接
哨兵会每10s发送一次心跳检测去检测获取服务器当前的状态信息
主服务器需要回复哨兵的心跳检测, 回复的内容包含主服务器当前的状态信息以及主服务器属下的从服务器信息
哨兵服务器可以通过主服务器的回复来更新哨兵中保存的服务器实例信息, 并及时建立与从服务器的连接
哨兵也会向已经建立连接的从服务器发送心跳检测来维护实例状态和检测掉线
Redis提供的分布式数据库方案, 集群会通过分片的方式来实现数据共享, 并提供复制和故障转移的功能
为什么要提供切片集群方案, 主从复制方案存在什么问题吗
主从复制模型最大的问题是日益膨胀的数据量很难通过硬件升级来满足
当服务器要缓存的数据量达到几十GB甚至几百GB的时候, 升级内存的价格就非常高了
同时因为RDB持久化的时候, 是通过阻塞主进程来创建子进程, 由子进程来负责完成持久化的, 如果缓存的数据量非常大的话, 创建子进程会导致主进程阻塞非常长的时间
考虑到上面两点, 所以就有了将数据切分分散到不同的服务器上的切片集群方案
Redis Cluster是Redis提供的切片集群方案, 重点关注这些方面
切片集群如何提供服务
切片集群的故障转移
Redis切片集群的缺点
切片集群会将所有的数据通过hash算法, 平分到16384个槽位中, 然后通过分配每个服务器负责的槽位来完成数据切割分发
每个Redis服务器都会记录当前节点自己的槽指派信息, 会使用一个16384位的位图来保存当前节点负责的槽
同时每个节点还会建立和其他节点的连接, 会通过消息将节点自己负责的槽记录发送给集群中的其他节点
节点接收到其他节点的信息后, 会将其保存起来
使用位图来保存节点自身负责的槽指派信息是方便传播给其他节点, 而使用数组来保存所有槽的指派信息是方便转发, 当发现当前请求的数据不在自己负责的槽中, 就可以直接通过本地存储的槽指派表来转发到正确的节点中
节点在和客户端建立连接后, 会将节点中保存的槽位分配信息表发送给客户端, 客户端会将这个表缓存起来, 用于在执行命令的之后知道应该向那个实例发送请求
客户端在执行命令的时候会计算出当前节点应该由哪个槽负责, 然后转发给对应的槽
注意槽不是固定不变了, 集群可能为了负载均衡或是集群的扩展收缩导致集群需要重新分配槽位
所以可能会出现客户端缓存的槽分配表过期的情况
先来看看重新分片, 这个是将节点负责的槽重新指派给另一个节点的操作
源节点会发送命令告知目标节点接下来要将自己负责的某个槽交给目标节点负责
然后源节点会将槽中对应的键值对发送给目标节点, 转移的过程中目标节点和源节点依旧可以处理命令
Redis是将所有的key打包成一条一条的命令发送给目标节点的
相当于正常处理命令的流程, 所以可以在不下线的情况下进行重新分片
重新分片完毕后目标节点就会向集群中发送消息告知槽的负责人的转移
客户端槽信息表过期怎么办
客户端是无法主动得知槽信息的变化的, 所以客户端只会按客户端本地缓存的槽信息表去发送请求
此时就需要集群节点来帮助客户端进行重新矫正定向
客户端在给某个节点发送消息的时候, 节点会检查客户端请求的key对应的槽位是否是自己负责的槽, 如果不是就会给客户端发送 MOVED
命令, 让客户端将命令转发到正确的节点
接受到MOVED
命令后, 客户端会同时更新缓存在客户端内的槽分配表信息
如果客户端请求的节点正好在进行槽转移会发送什么?
接收命令的节点会先检查客户端请求的key是否还在自己的数据库中, 如果没有就发送ASK
命令让客户端去转移后的节点去请求数据
注意发送的是ASK
命令, ASK
命令和MOVED
命令的区别在于MOVED
命令不会让客户端更新缓存的槽分配表, 因为可能下次客户端请求的key还在原来的旧节点中
Redis切片集群也是可以设置从节点的, 每个切片节点可以设置有多个从节点, 当主节点发生故障的时候就由从节点负责顶替主节点来提供服务
这里的检测过程可以查考上面的哨兵的检测过程
主从节点之间会通过PING-PONG
命令来进行信息的交互和网络检测
当从节点发现主节点掉线就会询问其他从节点, 当其他从节点同意主节点掉线就进行投票, 投票选中的节点就作为新的主节点
新主节点会发送消息告知其他从节点自己成为了新主节点, 并将原来主节点负责的槽位指派到自己身上并通知其他集群节点
转移完成后就可以开始接收槽相关的命令了
主要原因来自于Redis Cluster需要经常进行通信来交换集群中的节点信息, 用于检测节点的在线状况和维护槽分配表
每个节点默认都会每秒随机1个节点进行通信交互检测, 并且每0.1s还会扫描一次节点列表, 发现上次通信时间距今超过默认配置就会立刻发送消息进行交互检测
所以每秒每个节点起码有1条以上, 集群节点数量以下的消息在集群中传输
当集群的规模扩大后, 会因为网络阻塞 | 不同服务器之间的流量竞争原因, 导致节点与节点之间的通信延迟增加
当通信延迟增加后, 又会引发节点与节点之间大量传输消息, 给网络带来额外的开销
所以通信因素是影响Redis Cluster集群扩展的性能瓶颈
解决思路
节点之间需要大量的通信的原因是因为需要通过定期交互来维护槽分配表, 所以如果将槽分配表独立保存到另外的中间键上, 那么集群就不需要相互进行大量的消息交互来维护槽分配表了
Redis提供了多种集群方案来应对不同的环境, 主从复制和哨兵集群可以避免出现单点故障问题, 保证Redis一直会有一个节点提供服务
切片集群提供了更低成本的扩展性, 同时又利用主从复制和哨兵集群方案来保证切片集群的可靠性
以上就是Redis提供的三种集群方案了, 希望对大家的学习有所帮助