与其他的中间件存在同样的问题,在单机的情况,随着业务的增长,会面临着灾备、性能方面的压力。`Redis`在这方面提供了`一主一从`、`一主多从`的结构。这种结构同时也是实现`读写分离`功能的基础。即主节点提供写能力,从节点提供读能力。为了保证从节点的数据能和主节点的数据一致,`Redis`提供了`主从复制`的功能来实现。
1、单机的问题
1.1、机器故障
如果发生机器故障,例如磁盘、主板损坏,不能在短时间内修复好,客户端将无法连接到Redis
服务。这时我们的服务是不能正常使用的。
1.2、性能瓶颈
Redis
官方数据显示其可以达到10w的qps,如果业务需要更多的qps需求,怎么办?
1.3、小结
关于性能瓶颈是Redis
分布式需要解决的问题,机器故障就是高可用的问题。
2、主从复制的作用
Redis
提供两种主从的结构,一种是一主一从,另一种就是一主多从。从节点可以挂在主节点下,也可以挂在从节点下。
2.1、一主一从
一主一从即是一个主节点和一个从节点,主从节点都是可以对外提供服务的,从节点可以从主节点将变更的数据同步到自己上面。并且随着主节点的数据不断写入,从节点也会做响应的同步。
整体起到的就是数据备份的效果。
2.2、一主多从
一主多从是在一主一从的基础上,增加了多个从节点。这样就可以提供一个高可用的作用,比如主节点挂掉了,那么可以从多个从节点中选择出来一个作为主节点继续对外提供服务。
2.3、读写分离
除了作为数据备份功能,主从模型还能做另外一个功能就是读写分离。让master节点提供写服务,而将数据读取的压力分摊到多个读节点上。如果存在读数据上的压力,可以通过水平扩容的方式扩大从节点的数量。
2.4、主从复制的作用
- 数据备份:主从复制实现了数据的热备份
- 故障恢复:当主节点出现故障时,可以在从节点中选择一个作为主节点,继续对外提供服务。
- 负载均衡:在主从复制的基础上,配合读写分离。主节点提供写数据服务,从节点提供读数据服务(即写数据服务连接主节点,读数据服务连接从节点)。尤其在写少读多的情景下,通过多个从节点分担读数据压力,可以大大提高
Redis
服务器的并发量。 - 高可用基石:除了以上的功能,主从复制还是哨兵和
Redis
集群功能的基石。
2.5、小结
- 一个master可以有多个slave节点
- 一个slave节点只能有一个master节点
- 数据流向是单向的,即只能从主节点到从节点
3、主从复制的配置
3.1、slaveof命令
slaveof master_ip master_port
如图,如果想让6380成为6379的从节点,只需要执行slaveof
命令即可。此复制的命令时异步进行的,Redis
会自动进行后续的复制数据操作。
3.2、取消操作
如果6380不想成为6379的从节点,只需要执行slaveof no one
命令,取消后6380节点的数据不会清除,只能说6379节点后续不会同步数据到6380节点上。
如果6380重新挂在另外一个master节点上,那么新的主节点在同步数据给slave节点时,会先将从节点的数据删除。
3.3、小结
从节点开启主从复制有三种方式:
- 启动命令:redis-server启动命令时加入 --slaveof master_ip master_port
- 客户端命令:Redis服务器启动后,直接通过客户端执行命令:slaveof master_ip master_port,则该Redis实例成为从节点。
- 配置文件:在从服务器的配置文件中加入:slaveof master_ip master_port
# 配置主节点的IP和端口号
slaveof ip port
# 从节点只做读的操作,保证主从数据的一致性
slave-read-only yes
4、主从复制原理
为了避免单点故障,数据存储需要进行多副本存储。因此Redis
在面世起,就提供了复制功能,而且不断对复制策略进行优化。
通过数据复制,Redis的一个master节点下可以挂多个slave节点,而slave节点下还可以挂载多个slave节点。所有的写操作都在master节点上,master节点执行完毕,会将写命令分发到下面的slave节点,如果slave节点下还有其他slave节点,那么写命令会进一步下发到自己下面的slave节点上。
master在分发写请求时,同时会将写指令复制一份到**复制积压缓冲,这样当slave节点短时间断开重连时,只要slave节点的复制位置点在复制积压缓冲,**那么可以从之前的复制位置点继续进行复制,而不用全量同步。
主库master和从库slave之间通过复制ID进行匹配,避免slave挂载到错误的master。Redis
的复制分为全量同步和增量同步。
4.1、全量同步
Redis
全量同步一般发生在slave节点初始化阶段,这时slave需要将master节点上的所有数据都复制一份。
- 从服务器链接主服务器,发送SYNC命令。
- 主服务器接受到SYNC命令后,开始执行BGSAVE命令生成RDB文件,并使用复制积压缓冲来记录此后执行的所有写命令。
- 主服务器BGSAVE命令执行完之后,向所有的从服务器发送快照文件,并发送期间继续记录被执行的写命令。
- 从服务器收到快照文件之后,丢弃掉所有的旧数据,载入收到的数据。
- 主服务器发送完快照文件后,开始向从服务器发送复制积压缓冲中的写命令。
- 从服务器完成对快照文件的载入,开始接受命令请求,并执行来自主服务器复制积压缓冲的写命令。
完成以上的几个步骤之后就完成了slave节点的初始化操作,从服务器就可以接受来自用户的读请求。
在这个过程中,对master的性能损耗较大,slave节点构建数据的时间较长,而且传递rdb文件还会占用大量的带宽,对整个系统的性能和资源的访问影响较大。
4.2、增量同步
Redis
增量同步是指slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。
master只发送slave上次复制位置之后的写命令,不用构建rdb文件,而且传输内容非常有限,对master、slave的负荷影响很小,对整个系统的影响也非常小。
在Redis 2.8
之前,Redis基本上只支持全量复制。在slave与master断开连接,或者slave重启后(因为redis每次重启都会重新生成一个runid,且master和slave之间是根据runid进行匹配的),都需要进行全量同步。
在Redis 2.8
之后,Redis引入了psync命令,增加了一个复制积压缓冲,在将写命令同步给slave时,会同时在复制积压缓冲中写一份。在slave短时间断开重连后,上报master runid及复制偏移量,如果runid和master一致,且偏移量仍然在master的复制积压缓冲中,则进行增量同步。但是,如果slave重启后,master runid会丢失,仍然需要全量同步。
因此,在Redis 4.0
之后,强化了psync命令,引入了psync2。在psync2中,主从复制不再使用runid,而是使用replid(复制id)来作为复制判断依据。同时redis实例在构建rdb文件时,会将replid作为aux辅助信息存入rdb文件中。重启时,加载rdb文件,就可以得到master的复制id。从而在slave重启后仍然可以增量同步。
在psync2中,Redis
每个实例除了会有一个复制id即replid外,还有一个replid2。Redis启动时,会创建一个长度为40的随机字符串,作为replid的初值,在建立主从连接后,会用master的replid替换自己的replid。同时用replid2来存储上次master主库的replid。这样切主时,即便slave汇报的复制id与新master的replid不一样,但是和新master的replid2一样,同时复制偏移仍然在复制积压缓冲中,仍然可以实现增量同步(存疑)。
4.3、Redis主从同步策略
主从刚刚连接时,进行全量同步;全量同步结束后,进行增量同步。当然如果有需要,slave在任何时候都可以发起全量同步。
Redis
的策略是,无论如何先尝试进行增量同步,如不成功,要求从机进行全量同步。
4.4、Redis复制具体流程
5、主从复制的特点
- 采用异步复制。
- 一个主redis可以有多个从redis。
- 每个从redis可以接收来自其他从redis服务器的连接。
- 主从复制对主redis来说是非阻塞的,这意味着当从服务器进行主从复制过程中,主redis仍然可以对外提供服务。
- 主从复制对从redis来说也是非阻塞的,这意味着,即使从redis在进行主从复制,也可以接收外界的查询请求,只不过这时候从redis返回的数据是以前老的数据。当然也可以在配置文件中设置,从redis在同步过程中来自外界的请求都返回错误给客户端(从redis在接收到主redis的rdb文件后,将rdb文件的数据加载到内存的过程是阻塞)。
- 主从复制提高了redis服务的扩展性,避免单个redis服务器的读写访问压力过大的问题,同时也可以给数据备份及荣誉提供一种解决方案。
- 为了避免主redis服务器写磁盘压力带来的开销,可以配置让redis不在将数据持久化到磁盘,而是通过配置让一个从redis服务器及时的将相关的数据持久化到磁盘。不过这样会存在一个问题,就是主redis一旦重启,因为主redis没有持久化,因此在重启后,主redis的数据就会为空,这时,从redis进行同步,就会删除掉从redis上的数据。
6、主从同步中的几个问题
- 在上面的全量同步过程中,master会将数据保存在rdb文件中发送到从redis,但是master上的磁盘空间不够怎么办?
此时可以通过无盘复制来达到目的,由master直接开启一个socket将rdb文件发送给slave服务器。(无盘复制一般用在磁盘空间有限,但是网络状态良好的情况下)。
- 主从复制结构,一般slave服务器不能进行写操作,但是这不是死的,之所以这样是为了保证主和各个从之间数据的一致性。如果slave服务器进行了数据的修改,那么要保证所有主从服务器的数据一致,可能在结构上和处理逻辑更加复杂。当然也可以通过配置文件,不让从服务器提供写操作。
- 主从服务器之间会定期进行通过,如果master上设置了密码,那么如果不给slave设置密码,就会导致slave不能跟master进行任何操作,所以如果master有密码,那么也要给slave响应的设置下密码(通过配置文件中的masterauth)。
- 关于slave服务器上过期键的处理,由master服务器负责键的过期删除,然后将相关删除命令以数据同步的方式同步给slave服务器,slave服务器根据删除命令删除本地的key。
7、主服务器不行持久化时复制的安全性
在进行主从复制设置时,强烈建议在主服务器上开启持久化,当不能这么做时,应考虑将实例配置为避免自动重启。
为什么不持久化的主服务器自动重启非常危险?
假如设置节点A为主服务器,关闭持久化,节点B和C是节点A的从节点。
这时出现一个崩溃,但是Redis
具有自动重启系统,重启了进程,因为关闭了持久化,节点重启后只有一个空的数据集。
这时节点B和C从节点A进行复制,现在节点A的数据为空,所以节点B和C的数据也会为空。
当在高可用系统中使用Redis Sentinel,关闭主服务器的持久化,并且允许自动重启,这种情况是很危险的。
比如主服务器在很短的时间就完成了重启,以至于Sentinel都无法检测到这次失败,那么上面说的这种失败情况就会发生。如果数据比较重要,并且在使用主从复制过程时,关闭了主服务器持久化功能的场景中,都应该关闭实例的自动重启功能。
8、只读从服务器
从Redis 2.6开始,从服务器支持只读模式,且是默认模式。这个行为是由redis.conf文件中的slave-read-only参数控制的。
只读的从服务器会拒绝所有的写命令,所以对从服务器不会有写操作。但是这不表示可以把服务器暴露在危险的网络环境下。
因为像DEBUG和CONFIG这样的管理命令还是可以运行。不过可以通过rename-command命令来为这些命令进行重命名。