@[Toc]


1. 单机架构

单机架构模式中只有一个Redis服务器,它既负责数据的存储,也负责数据的读取。架构示意如下所示:
Redis单机和主从架构 - 图1

但是在实际的应用场景中并不会使用单机架构模式,它无法有效的解决如下的问题:

  • 数据量伸缩问题:如果将Redis作为缓存数据库使用,我们可以通过设置缓存失效时间,以及不同的淘汰策略来清除失效的数据,避免数据常占Redis的内存空间。但是,当Redis作为一个NoSQL数据库使用时,随着数据量的增大,单机模式显然无法满足要求
  • 访问量伸缩问题:Redis是基于单线程运行的,当读写访问请求很多时,Redis节点无法满足正常的响应时间要求
  • 节点故障问题:当Redis节点发生宕机时,无论是读请求还是写请求,节点都无法正常提供服务

2. 主从架构

主从架构示意图如下所示:
Redis单机和主从架构 - 图2

2.1 配置

下面我们同样通过docker-compsoe来搭建Redis主从架构模式的服务器。首先,编写对应的docker-compose.yml文件,内容如下:

  1. version: '3.1'
  2. services:
  3. # master
  4. redis1:
  5. image: daocloud.io/library/redis:5.0.7
  6. restart: always
  7. container_name: redis1
  8. environment:
  9. - TZ=Asia/Shanghai
  10. ports:
  11. - 7001:6379
  12. volumes:
  13. - ./conf/redis1.conf:/usr/local/redis/redis.conf
  14. command: ["redis-server", "/usr/local/redis/redis.conf"]
  15. # slave1
  16. redis2:
  17. image: daocloud.io/library/redis:5.0.7
  18. restart: always
  19. container_name: redis2
  20. environment:
  21. - TZ=Asia/Shanghai
  22. ports:
  23. - 7002:6379
  24. volumes:
  25. - ./conf/redis2.conf:/usr/local/redis/redis.conf
  26. # 通过links指定它的master是谁
  27. links:
  28. - redis1:master
  29. command: ["redis-server", "/usr/local/redis/redis.conf"]
  30. # slave2
  31. redis3:
  32. image: daocloud.io/library/redis:5.0.7
  33. restart: always
  34. container_name: redis3
  35. environment:
  36. - TZ=Asia/Shanghai
  37. ports:
  38. - 7003:6379
  39. volumes:
  40. - ./conf/redis3.conf:/usr/local/redis/redis.conf
  41. links:
  42. - redis1:master
  43. command: ["redis-server", "/usr/local/redis/redis.conf"]

相应的需要为每一个节点准备一个配置文件,并且在redis2.conf和redis3.conf中添加salve节点相应的配置。

  1. # 从节点配置
  2. replicaof master 6379

最后启动容器,通过命令进入到mater节点后,使用info命令查看节点信息可以看到,当前节点为master,而且它有两个slave,对应的ip和port也可以看到。

  1. # Replication
  2. role:master
  3. connected_slaves:2
  4. slave0:ip=172.23.0.3,port=6379,state=online,offset=56,lag=0
  5. slave1:ip=172.23.0.4,port=6379,state=online,offset=56,lag=0

然后进入一个slave节点,同样使用info命令查看节点信息:

  1. # Replication
  2. role:slave
  3. master_host:master
  4. master_port:6379
  5. master_link_status:up
  6. master_last_io_seconds_ago:3
  7. master_sync_in_progress:0

2.2 原理

主从复制是指将一个Redis节点上的数据,复制到其他的 Redis节点上。前者称为 主节点(master),后者称为 从节点(slave)。而且数据的复制是 单向 的,只能由主节点到从节点。Redis 主从复制支持 主从同步从从同步 两种。主从复制的优势在于:

  • 数据冗余: 主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式
  • 故障恢复 :当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复
  • 负载均衡: 在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务。在写少读多的场景下,通过多个从节点分担读请求,可以大大提高 Redis节点的并发量
  • 高可用基石 :主从复制还是哨兵模式和集群模式能够实施的基础

整个主从复制的原理如下所示:
Redis单机和主从架构 - 图3
source

整个过程可以分为准备阶段数据同步阶段命令传播三个阶段。其中同步操作有SYNCPSYNC两个命令可以选择使用,其中SYNC命令消耗资源非常多,每次执行命令,master节点都需要执行如下的操作:

  • master生成RDB快照,消耗大量的CPU、内存和磁盘IO资源
  • master节点需要将生成的RDB文件发送给slave节点,这又会消耗大量的网络资源,并且会影响master节点的响应时间
  • slave节点在收到RDB文件后需要载入,载入期间会因为阻塞而没办法处理读命令请求

PSYNC命令有两种模式:

  • 全量复制 :用于初次复制或者无法部分复制的场景,它会将master节点的数据全部发送给slave节点
  • 部分复制:用于网络中断后的复制,只将中断期间master节点执行的写命令发送给slave节点。如果中断时间太长,导致master无法完整的保存中断期间执行的写命令,那么仍会使用部分复制

sync负责把缓存区上的东西排到写队列中(缓冲区->写队列),在由守护进程负责把队列里的东西写到磁盘上,而sync函数在把缓存区上的东西排到写队列后,不管写队列中的内容是否写到磁盘上都立即返回。fsync函数则是对指定文件的操作,而且必须等到写队列中的内容都写到磁盘后才返回,并且更新文件inode结点里的内容。

下面着重看一下PSYNC的主要流程,如下所示:
Redis单机和主从架构 - 图4

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命令,那么将再执行一次复制操作。