Redis 分布式 主从复制

读写分离

对于读多写少的场景,可以使用读写分离的方式来提高整体的并发量,对于 Redis 也一样,由于从节点是主节点的副本,可以利用主节点提供写服务,一个或者多个从节点提供读服务,这样就最大化 Redis 的读负载能力。当然,这样并不是说主从架构下的读写分离没有问题,恰恰是有大问题,在这种情况下,业务需要面对如下几个问题:

  • 数据延迟
  • 读到过期数据
  • 从节点故障

数据延迟
在命令传送阶段,Redis 主从节点同步命令的过程是异步的,所以势必会导致主从节点的数据不一致性,如果应用程序对数据的不一致性接受程度不是很高,则可以从以下几个方面优化:

  1. 优化主从节点的网络环境(比如主从节点部署在同机房)
  2. 监控从节点的延迟性,如果延迟过大,则通知应用不从该节点读取数据,这种方案需要改造客户端,工作量不小,一般不推荐

从节点的 slave-serve-stale-data 参数便与其有关,它控制着从节点对读请求的响应情况,如果为 yes(默认值),则从节点可以响应客户端的命令,如果为 NO,则只能响应 info、slaveof 等少数命令。所以如果应用对数据一致性要求较高,则应该将该参数设置为 NO。
读到过期数据
Redis 内部对过期数据的删除有两种方案:惰性删除、定时删除。

  • 惰性删除:服务器不会主动删除数据,只有当客户端查询某个数据时,服务器判断该数据是否过期,过期则删除
  • 定时删除:服务器内部维护着一个定时任务,会定时删除过期的数据

在主从复制的架构模式下,所有的写操作都发生在主节点,删除数据时也是在主节点删除,然后将删除命令同步给从节点,由于主从节点的延迟性,主节点删除了某些数据,从节点不一定也删除了,所以在从节点是比较容易读到过期的数据。不过在 Redis 3.2 版本中,增加了对数据是否过期的判断,如果读到的数据过期了,则不会返回给客户端了,所以将 Redis 升级到 3.2 版本后即可解决该问题。
从节点故障
如果从节点出现故障了,在没有哨兵的情况下,客户端是无法进行切换的,所以需要对客户端进行改造,它内部需要维护一个可用的 Redis 节点列表,当某个节点出现故障不可用后,立刻切换到其他可用节点,当然这种工作量会比较大,得不偿失。

规避全量复制

全量复制要经过如下几个过程:建立连接 —-> 生成 RDB 文件 —-> 发送 RDB 文件 —-> 清空旧数据 —-> 加载新数据,五个过程,工作量巨大,是一个非常消耗资源的操作,所以需要尽可能的规避。有如下几种情况会发生全量复制:

  • 第一次复制
  • 节点运行 ID 不匹配
  • 复制偏移量 offset 不在复制积压缓冲区中

第一次复制
这种情况是无法避免的,所以如果要对数据量较大且流量较高的主节点添加从节点的话,最好是选择在低峰值进行操作。
节点运行 ID 不匹配
主节点重启时,他的 runid 会发生改变,在进行复制的时候,发现主从节点的 runid 不一致,则进行全量复制,对于这种情况一般都应该在架构上面规避,比如提供故障转移的功能。还有一种情况就是修改了主节点的配置,需要重启才能够生效,这个时候如果重启主节点发生全量复制就得不偿失了,可以选择安全重启的方式(debug reload),在这种情况,主节点重启的 runid 是不会发生改变的。
复制偏移量 offset 不在复制积压缓冲区中
当主从节点因网络中断而重新连接,从节点会发送 psync 命令进行部分复制请求,主节点除了校验 runid 是否一致外还会判断请求的 offset 是否在缓冲区中,如果不在这进行全量复制。复制积压缓冲区默认大小为 1MB,这对于高流量的主节点而言势必显得有点儿小了,所以为了避免全量复制,需要根据中断时长来调整复制积压缓冲区的大小,调整为 复制积压缓冲区大小>平均中断时长*平均写命令字节数,这样就可以避免因为复制积压缓冲区不足而导致的全量复制

规避复制风暴

复制风暴指的是大量从节点对同一主节点或者同一台服务器的多个主节点短时间内发起全量复制的过程。复制风暴会导致主节点消耗大量的 CPU、内存、宽带,所以需要尽可能的规避复制风暴。
单一主节点
这种情况一般都发生在一个主节点挂载了多个从节点,所以规避的方案也比较简单:

  • 减少挂载的从节点
  • 将架构调整为树状结构,增加中间层,但是这样导致的后果是中间层越多,后面节点的数据延迟就越高,同时也增加了运维的难度

单一机器
Redis 的单线程运行的,所以会存在一台机器上面部署多个 Redis 服务,同时也有多个是主节点,所以规避方案:

  • 将主节点分散在多台不同的机器上
  • 当主节点所在机器故障后提供故障转移机制,避免机器回复后进行密集的全量复制

    主从复制的相关配置

  1. slaveof :Redis启动时起作用;作用是建立复制关系,开启了该配置的 Redis 服务器在启动后成为从节点。该注释默认注释掉,即 Redis 服务器默认都是主节点。
  2. repl-timeout 60:与各个阶段主从节点连接超时判断有关。

主节点相关配置

  1. repl-diskless-sync no:作用于全量复制阶段,控制主节点是否使用diskless复制(无盘复制)。所谓diskless复制,是指在全量复制时,主节点不再先把数据写入RDB文件,而是直接写入slave的socket中,整个过程中不涉及硬盘;diskless复制在磁盘IO很慢而网速很快时更有优势。需要注意的是,截至Redis3.0,diskless复制处于实验阶段,默认是关闭的。
  2. repl-diskless-sync-delay 5:该配置作用于全量复制阶段,当主节点使用diskless复制时,该配置决定主节点向从节点发送之前停顿的时间,单位是秒;只有当diskless复制打开时有效,默认5s。之所以设置停顿时间,是基于以下两个考虑:(1)向slave的socket的传输一旦开始,新连接的slave只能等待当前数据传输结束,才能开始新的数据传输 (2)多个从节点有较大的概率在短时间内建立主从复制。
  3. client-output-buffer-limit slave 256MB 64MB 60:与全量复制阶段主节点的缓冲区大小有关。
  4. repl-disable-tcp-nodelay no:与命令传播阶段的延迟有关。
  5. masterauth :与连接建立阶段的身份验证有关。
  6. repl-ping-slave-period 10:与命令传播阶段主从节点的超时判断有关。
  7. repl-backlog-size 1mb:复制积压缓冲区的大小。
  8. repl-backlog-ttl 3600:当主节点没有从节点时,复制积压缓冲区保留的时间,这样当断开的从节点重新连进来时,可以进行全量复制;默认3600s。如果设置为0,则永远不会释放复制积压缓冲区。
  9. min-slaves-to-write 3与min-slaves-max-lag 10:规定了主节点的最小从节点数目,及对应的最大延迟。

从节点相关配置

  1. slave-serve-stale-data yes:与从节点数据陈旧时是否响应客户端命令有关。
  2. slave-read-only yes:从节点是否只读;默认是只读的。由于从节点开启写操作容易导致主从节点的数据不一致,因此该配置尽量不要修改。