redis集群原理及搭建与使用:https://juejin.im/post/5ad54d76f265da23970759d3
一、主从架构
在Redis中,用户可以通过执行SALVEOF命令或者设置salveof选项,让一个服务器去复制(replicate)另一个服务器,我们称呼被复制的服务器为主服务器(master),而对主服务器进行复制的服务器则被称为从服务器(salve)
1. 主从架构形式
- 主服务器负责接收写请求
- 从服务器负责接收读请求
- 从服务器的数据由主服务器复制过去。主从服务器的数据是一致的
主从架构除了上面的形式,也有下面这种的(只不过用得比较少):
2. 主从架构优点
- 读写分离(主服务器负责写,从服务器负责读)
- 高可用(某一台从服务器挂了,其他从服务器还能继续接收请求,不影响服务)
- 处理更多的并发量(每台从服务器都可以接收读请求,读QPS就上去了)
二、复制功能
1. 复制功能的具体实现
复制功能分为两个操作:
- 同步(sync)
将从服务器的数据库状态更新至主服务器的数据库状态
- 命令传播(command propagate)
主服务器的数据库状态被修改,导致主从服务器的数据库状态不一致,让主从服务器的数据库状态重新回到一致状态。
从服务器对主服务器的同步又可以分为两种情况:
- 初次同步:从服务器没有复制过任何的主服务器,或者从服务器要复制的主服务器跟上次复制的主服务器不一样。
- 断线后同步:处于命令传播阶段的主从服务器因为网络原因中断了复制,从服务器通过自动重连重新连接主服务器,并继续复制主服务器
在Redis2.8以前,断线后复制这部分其实缺少的只是部分的数据,但是要让主从服务器重新执行SYNC命令,这样的做法是非常低效的。(因为执行SYNC命令是把所有的数据再次同步,而不是只同步丢失的数据)
接下来我们来详细看看Redis2.8以后复制功能是怎么实现的:
2. 复制的前置工作
- 从服务器设置主服务器的IP和端口
- 建立与主服务器的Socket连接
- 发送PING命令(检测Socket读写是否正常与主服务器的通信状况)
- 身份验证(看有没有设置对应的验证配置)
- 从服务器给主服务器发送端口的信息,主服务器记录监听的端口
前面也提到了,Redis2.8之前,断线后同步会重新执行SYNC命令,这是非常低效的。下面我们来看一下Redis2.8之后是怎么进行同步的。
Redis从2.8版本开始,使用PSYNC命令来替代SYNC命令执行复制时同步的操作。
PSYNC命令具有完整重同步和部分重同步两种模式(其实就跟上面所说的初次复制和断线后复制差不多个意思)。
3. 完整重同步
- 从服务器向主服务器发送PSYNC命令
- 收到PSYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件。并用一个缓冲区来记录从现在开始执行的所有写命令。
- 当主服务器的BGSAVE命令执行完后,将生成的RDB文件发送给从服务器,从服务器接收和载入RBD文件。将自己的数据库状态更新至与主服务器执行BGSAVE命令时的状态。
- 主服务器将所有缓冲区的写命令发送给从服务器,从服务器执行这些写命令,达到数据最终一致性。
4. 部分重同步
接下来我们来看看部分重同步,部分重同步可以让我们断线后重连只需要同步缺失的数据(而不是Redis2.8之前的同步全部数据),这是符合逻辑的!
部分重同步功能由以下部分组成:
- 主从服务器的复制偏移量
- 主服务器的复制积压缓冲区
- 服务器运行的ID(run ID)
首先我们来解释一下上面的名词:
复制偏移量:执行复制的双方都会分别维护一个复制偏移量
- 主服务器每次传播N个字节,就将自己的复制偏移量加上N
- 从服务器每次收到主服务器的N个字节,就将自己的复制偏移量加上N
通过对比主从复制的偏移量,就很容易知道主从服务器的数据是否处于一致性的状态!
那断线重连以后,从服务器向主服务器发送PSYNC命令,报告现在的偏移量是36,那么主服务器该对从服务器执行完整重同步还是部分重同步呢??这就交由复制积压缓冲区来决定。
当主服务器进行命令传播时,不仅仅会将写命令发送给所有的从服务器,还会将写命令入队到复制积压缓冲区里面(这个大小可以调的)。如果复制积压缓冲区存在丢失的偏移量的数据,那就执行部分重同步,否则执行完整重同步。
服务器运行的ID(run ID)实际上就是用来比对ID是否相同。如果不相同,则说明从服务器断线之前复制的主服务器和当前连接的主服务器是两台服务器,这就会进行完整重同步。
5. 命令传播
当完成了同步之后,主从服务器就会进入命令传播阶段。这时主服务器只要将自己的写命令发送给从服务器,而从服务器接收并执行主服务器发送过来的写命令,就可以保证主从服务器一直保持数据一致了!
在命令传播阶段,从服务器默认会以每秒一次的频率,向服务器发送命令REPLCONF ACK <replication_offset> 其中replication_offset是从服务器当前的复制偏移量
发送这个命令主要有三个作用:
- 检测主从服务器的网络状态
- 辅助实现min-slaves选项
- 检测命令丢失
三、主备切换(故障转移)
1. 哨兵(Sentinal)机制
Redis提供了哨兵(Sentinal)机制。如果主服务器挂了,我们可以将从服务器升级为主服务器,等到旧的主服务器(挂掉的那个)重连上来,会将它(挂掉的主服务器)变成从服务器。
- 这个过程叫做主备切换(故障转移)
哨兵(Sentinal)机制主要用于实现Redis的高可用性,主要的功能如下:
- Sentinel不停地监控Redis主从服务器是否正常工作
- 如果某个Redis实例有故障,那么哨兵负责发送消息通知管理员
- 如果主服务器挂掉了,会自动将从服务器提升为主服务器(包括配置都会修改)。
- Sentinel可以作为配置中心,能够提供当前主服务器的信息。
tips:Sentinel可以让我们的Redis实现高可用,Sentinel作为这么一个组件,自身也必然是高可用的(不可能是单点的)
下面来具体讲讲Sentinel是如何将从服务器提升为主服务器的。
2. 启动和初始化哨兵 Sentinel
首先我们要知道的是:Sentinel本质上只是一个运行在特殊模式下的Redis服务器。因为Sentinel做的事情和Redis服务器是不一样的,所以它们的初始化是有所区别的(比如,Sentinel在初始化的时候并不会载入AOF/RDB文件,因为Sentinel根本就不用数据库)。
然后,在启动的时候会将普通Redis服务器的代码替换成Sentinel专用代码。(所以Sentinel虽然作为Redis服务器,但是它不能执行SET、DBSIZE等等命令,因为命令表的代码被替换了)
接着,初始化Sentinel的状态,并根据给定的配置文件初始化Sentinel监视的主服务器列表。
最后,Sentinel会创建两个连向主服务器的网络连接:
- 命令连接(发送和接收命令)
- 订阅连接(订阅主服务器的_sentinel_:hello频道)
3. 获取和更新信息
Sentinel通过主服务器发送INFO命令来获得主服务器属下所有从服务器的地址信息,并为这些从服务器创建相应的实例结构。当发现有新的从服务器出现时,除了创建对应的从服务器实例结构,Sentinel还会创建命令连接和订阅连接。
在Sentinel运行的过程中,通过命令连接会以每两秒一次的频率向监视的主从服务器的_sentinel_:hello频道发送命令(主要发送Sentinel本身的信息,监听主从服务器的信息),并通过订阅连接接收_sentinel_:hello频道的信息。
这样一来一回,我们就可以更新每个Sentinel实例结构的信息。
4. 判断主服务器是否下线了
判断主服务器是否下线有两种情况:
- 主观下线
- Sentinel会以每秒一次的频率向与它创建命令连接的实例(包括主从服务器和其他的Sentinel)发送PING命令,通过PING命令返回的信息判断实例是否在线
- 如果一个主服务器在down-after-milliseconds毫秒内连续向Sentinel发送无效回复,那么当前Sentinel就会主观认为该主服务器已经下线了。
- 客观下线
- 当Sentinel将一个主服务器判断为主观下线以后,为了确认该主服务器是否真的下线,它会向同样监视该主服务器的Sentinel询问,看它们是否也认为该主服务器是否下线。
- 如果足够多的Sentinel认为该主服务器是下线的,那么就判定该主服务为客观下线,并对主服务器执行故障转移操作。
在多少毫秒内无效回复才认定主服务器是主观下线的,以及有多少个Sentinel认为主服务器是下线才认定为客观下线。这都是可以配置的
5. 选举领头Sentinel和故障转移
当一个主服务器认为为客观下线以后,监视这个下线的主服务器的各种Sentinel会进行协商,选举出一个领头的Sentinel,领头的Sentinel会对下线的主服务器执行故障转移操作。
选举领头Sentinel的规则也比较多,总的来说就是先到先得(哪个快,就选哪个)
选举出领头的Sentinel之后,领头的Sentinel会对已下线的主服务器执行故障转移操作,包括三个步骤:
- 在已下线主服务器属下的从服务器中,挑选一个转换为主服务器
- 让已下线主服务器属下的所有从服务器改为复制新的主服务器
- 已下线的主服务器重新连接时,让他成为新的主服务器的从服务器
挑选某一个从服务器作为主服务器也是有策略的,大概如下:
4. 跟master断开连接的时长
5. slave优先级
6. 复制offset
7. run id
6. 丢失数据
目前为止的主从+哨兵架构可以说Redis是高可用的,但要清楚的是:Redis还是会丢失数据的
丢失数据有两种情况:
- 异步复制导致的数据丢失
- 有部分数据还没复制到从服务器,主服务器就宕机了,此时这些部分数据就丢失了
- 脑裂导致的数据丢失
- 有时候主服务器脱离了正常网络,跟其他从服务器不能连接。此时哨兵可能就会认为主服务器下线了(然后开启选举,将某个从服务器切换成了主服务器),但是实际上主服务器还运行着。这个时候,集群里就会有两个服务器(也就是所谓的脑裂)。
- 虽然某个从服务器被切换成了主服务器,但是可能客户端还没来得及切换到新的主服务器,客户端还继续写向旧主服务器写数据。旧的服务器重新连接时,会作为从服务器复制新的主服务器(这意味着旧数据丢失)。
可以通过以下两个配置尽量减少数据丢失的可能:
min-slaves-to-write 1
min-slaves-max-lag 10