@[Toc]
1. 单机架构
单机架构模式中只有一个Redis服务器,它既负责数据的存储,也负责数据的读取。架构示意如下所示:
但是在实际的应用场景中并不会使用单机架构模式,它无法有效的解决如下的问题:
- 数据量伸缩问题:如果将Redis作为缓存数据库使用,我们可以通过设置缓存失效时间,以及不同的淘汰策略来清除失效的数据,避免数据常占Redis的内存空间。但是,当Redis作为一个NoSQL数据库使用时,随着数据量的增大,单机模式显然无法满足要求
- 访问量伸缩问题:Redis是基于单线程运行的,当读写访问请求很多时,Redis节点无法满足正常的响应时间要求
- 节点故障问题:当Redis节点发生宕机时,无论是读请求还是写请求,节点都无法正常提供服务
2. 主从架构
主从架构示意图如下所示:
2.1 配置
下面我们同样通过docker-compsoe来搭建Redis主从架构模式的服务器。首先,编写对应的docker-compose.yml文件,内容如下:
version: '3.1'services:# masterredis1:image: daocloud.io/library/redis:5.0.7restart: alwayscontainer_name: redis1environment:- TZ=Asia/Shanghaiports:- 7001:6379volumes:- ./conf/redis1.conf:/usr/local/redis/redis.confcommand: ["redis-server", "/usr/local/redis/redis.conf"]# slave1redis2:image: daocloud.io/library/redis:5.0.7restart: alwayscontainer_name: redis2environment:- TZ=Asia/Shanghaiports:- 7002:6379volumes:- ./conf/redis2.conf:/usr/local/redis/redis.conf# 通过links指定它的master是谁links:- redis1:mastercommand: ["redis-server", "/usr/local/redis/redis.conf"]# slave2redis3:image: daocloud.io/library/redis:5.0.7restart: alwayscontainer_name: redis3environment:- TZ=Asia/Shanghaiports:- 7003:6379volumes:- ./conf/redis3.conf:/usr/local/redis/redis.conflinks:- redis1:mastercommand: ["redis-server", "/usr/local/redis/redis.conf"]
相应的需要为每一个节点准备一个配置文件,并且在redis2.conf和redis3.conf中添加salve节点相应的配置。
# 从节点配置replicaof master 6379
最后启动容器,通过命令进入到mater节点后,使用info命令查看节点信息可以看到,当前节点为master,而且它有两个slave,对应的ip和port也可以看到。
# Replicationrole:masterconnected_slaves:2slave0:ip=172.23.0.3,port=6379,state=online,offset=56,lag=0slave1:ip=172.23.0.4,port=6379,state=online,offset=56,lag=0
然后进入一个slave节点,同样使用info命令查看节点信息:
# Replicationrole:slavemaster_host:mastermaster_port:6379master_link_status:upmaster_last_io_seconds_ago:3master_sync_in_progress:0
2.2 原理
主从复制是指将一个Redis节点上的数据,复制到其他的 Redis节点上。前者称为 主节点(master),后者称为 从节点(slave)。而且数据的复制是 单向 的,只能由主节点到从节点。Redis 主从复制支持 主从同步 和 从从同步 两种。主从复制的优势在于:
- 数据冗余: 主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式
- 故障恢复 :当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复
- 负载均衡: 在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务。在写少读多的场景下,通过多个从节点分担读请求,可以大大提高 Redis节点的并发量
- 高可用基石 :主从复制还是哨兵模式和集群模式能够实施的基础
整个主从复制的原理如下所示:
source
整个过程可以分为准备阶段、数据同步阶段和命令传播三个阶段。其中同步操作有SYNC和PSYNC两个命令可以选择使用,其中SYNC命令消耗资源非常多,每次执行命令,master节点都需要执行如下的操作:
- master生成RDB快照,消耗大量的CPU、内存和磁盘IO资源
- master节点需要将生成的RDB文件发送给slave节点,这又会消耗大量的网络资源,并且会影响master节点的响应时间
- slave节点在收到RDB文件后需要载入,载入期间会因为阻塞而没办法处理读命令请求
PSYNC命令有两种模式:
- 全量复制 :用于初次复制或者无法部分复制的场景,它会将master节点的数据全部发送给slave节点
- 部分复制:用于网络中断后的复制,只将中断期间master节点执行的写命令发送给slave节点。如果中断时间太长,导致master无法完整的保存中断期间执行的写命令,那么仍会使用部分复制
sync负责把缓存区上的东西排到写队列中(缓冲区->写队列),在由守护进程负责把队列里的东西写到磁盘上,而sync函数在把缓存区上的东西排到写队列后,不管写队列中的内容是否写到磁盘上都立即返回。fsync函数则是对指定文件的操作,而且必须等到写队列中的内容都写到磁盘后才返回,并且更新文件inode结点里的内容。
下面着重看一下PSYNC的主要流程,如下所示:
master节点端的操作如下:
- 首先,slave节点向自己的master节点发起
sync命令,master将新进的slave节点加入到自己的slave列表中,,然后执行bgsave来进行持久化操作 - 持久化操作结束后,将得到的快照发送给slave节点。发送期间,如果master节点收到来自客户端的写命令,除了正常的响应外,还会存一份到back-log中
- 当快照发送结束后,还将back-log队列中的信息发送给salve节点。全部发送结束后,master节点后续的写操作同时发送给slave节点,保持实时的异步复制
slave节点端的操作如下:
- 当slave节点发送
sync命令后,继续对外提供读服务 - 当slave节点收到快照后,将slave现有的数据清空,再将快照写入到自己的内存空间。同时接收back-log,执行重放操作,期间继续执行读服务
- 重放完成后,slave节点将继续接收master节点的命令副本并继续重放,保证主从之间的数据一致性
如果master节点对应的多个slave节点发送了多个
sync命令,只要在master节点bgsave操作执行结束之前到达,那么这些salve节点收到的快照是一样的。如果master节点在执行了一次主从复制后,收到了新的sync命令,那么将再执行一次复制操作。
