redis 复制功能说明

2015-01-26

redis 复制功能说明

一. 概述

redis的复制功能可以分为同步和命令这两阶段操作, 同步操作使得 slave 的状态更新到 master 当前所在的状态, 命令传播阶段则实现增量的状态更新, 使得 master 和 slave 达到一致的状态.

在复制功能的实现中, 同步即对应着初始复制阶段, 命令传播对应着增量复制阶段. 在 client 向 slave 发送 slaveof 命令时, 会使得 slave 开始复制 master, 首先 slave 需要执行初始复制, 即上述的同步阶段, 同步过程的通信流程如下:

  1. slave 发送 SYNC 给 master;
  2. master 收到 SYNC 后执行 BGSAVE, 生成 RDB 文件, 且使用缓冲区记录从当前开始执行的所有 write 操作;
  3. master 发送 RDB 文件给 slave, slave 接收并加载 RDB 文件, 此时 slave 状态更新到 master 执行 BGSAVE 时的状态;
  4. master 发送缓冲区的命令给 slave, slave 执行命令, 此时 slave 状态更新到 master 当前所处的状态.

初始复制完成后, 在 master 进行了更新操作,为确保 master 和 slave 之间的数据一致, master 需要将更新的命令发送给 slave, slave 执行后完成增量复制阶段.

二. 复制功能

不过对于增量复制的实现, redis 的新旧版实现方式确有很大不同, 2.8之前为旧版, 反之为新版.

旧版复制功能(2.8版本之前)

1. 初始复制(同上述的同步过程)
2. 增量复制(命令阶段同上, 但是在主从故障恢复后, 比如网络中断恢复, slave 向 master 继续发送SYNC, 重新执行初始复制过程,并不是我们认为的增量复制 )

主从中断后的复制机制, 发送SYNC命令, 所以在旧版中,一旦有主从中断的发生就会引起很多不必要的操作. 当然在2.8版本后这种方式得到了较好的解决.

新版复制功能(2.8及之后版本)

复制的整体过程类同上述所说, 不过使用 PSYNC 来代替 SYNC, 增量复制由以下部分构成:

1. master 复制偏移量 ( replication offset ), 使用偏移量完成增量复制.
2. master 复制积压缓冲区( replication backlog ) : 参数repl-backlog-size, 
3. master 运行 ID ( run ID ): 机器启动后自动生成, 重启的时候会改变, slave同坐做全量同步(SYNC).

master 的复制缓冲区中保存最近执行的更新命令,里面也保存着复制的偏移量, 主从断线后的复制机制, slave 发送 PSYNC 命令, 通过偏移值完成增量复制(如果 slave 的偏移值还在缓冲区中则进行增量复制, 反之执行初始复制.) 可以加大 repl-backlog-size 和 repl-backlog-ttl 参数的值来延长缓冲区失效的时间.

注意: 如果 master 或 slave 重启了则进行初始复制(同步操作),因为丢失了偏移值, 算是比较悲催的特性.

总结来看, 在旧版本中, master, slave 或网络的中断等原因都会引起初始复制, 新版本中网络中断等原因则进行增量复制.

三. 其它

vip高可用

redis 服务通过 vip 对外服务, 需要注意 conf 配置中的 bind 选项需要监听所有端口, 比如仅监听一个内网地址, 在做主从切换后, 新的 master 的vip 不会生效,

内核参数设置

redis 在内存不足的时候进行 bgsave 等操作的时候可能出现下面的错误:

5605:M 24 Aug 21:46:29.085 # Can't save in background: fork: Cannot allocate memory
5605:M 24 Aug 21:46:35.100 * 1000000 changes in 60 seconds. Saving...

避免此类错误可以调整 vm.overcommit_memory 参数为 1 以通知系统有足够的内存可供使用, 不过这种方式可能导致系统中的其它进程被 OOM, 稳妥点的可以设置 vm.overcommit_memory 为 2, 可用的内存通过公式 swap + ((total - total TLB) * overcommit_ratio)/100 进行计算, 在有 swap 的环境中, 这种方式会引起很多的 swap 交换, 延长 redis save 的操作时长, 另外也可以适当调整 overcommit_ratio 加大可用内存的容量, 最后通过 /proc/meminfo 来查看设置是否生效:

# disanle vm, bgsave may fail under low memory condition 
sysctl -w vm.overcommit_memory=1
sysctl -w vm.overcommit_ratio=75

# grep 'Commit' /proc/meminfo     
CommitLimit:    79753448 kB # 最大内存
Committed_AS:   19737384 kB # 系统目前分配的内存大小

# 2.8v , redis must be restart after THP( Transparent_hugepage ) is disabled
echo never > /sys/kernel/mm/transparent_hugepage/enabled

# tcp backlog should less than the value of somaxconn 
sysctl -w net.core.somaxconn=600

OOM问题

如果担心系统的评分机制杀掉 redis 进程, 可以设置 redis 进程不参与评分, 以下操作:

echo '-17' >/proc/`pidof redis`/oom_adj

master性能

有些案例建议在 master 配置中禁掉 SNAPSHOTTING, 即不让触发 redis 的 save 操作, 避免引起不是必要的性能损耗, 同时禁止aof等操作, 但是在 slave 中开启 save (和 aof功能), 这样能更大程度的提高 master 的性能, 也能减少故障引起的数据丢失. 另外 slave 故障恢复后, 由于网络, redis 数据文件的大小, 以及 slave 加载数据文件等原因, 应该适当增加 repl-timeout 参数值确保 slave 有足够的时间进行重做操作, 否则可能会引起重复的初始复制操作.

replication ack 检测

在命令传播阶段, slave 以每秒一次的频率向 master 发送检测命令, 主要有以下作用

检测主从网络连接状态

在 master 中执行 info replication :

role:master
connected_slaves:1
slave0:ip=10.0.21.7,port=6380,state=online,offset=4299,lag=1

lag值表示 slave 最后一次向 master 发送检测命令距离现在过了多少秒, 正常应该在0或1之间跳动.

通过lag实现min-slaves配置选项

# slave 数量少于3, 或 3 个 slave 的延迟都 >= 10 秒时, master将拒绝执行写命令.
min-slaves-to-write 3
min-slaves-max-lag 10

检测命令丢失

如果 master 传播给 slave 的命令丢失, 当 slave 向 master 发送检测命令的时候, master 会发现 slave 当前的偏移量 offset 小于自己的复制偏移量, 之后 master 会根据 slave 的偏移信息, 在复制积压缓冲区里面找到 slave 缺少的数据, 并重新发送给 slave.

ref: redisbook.com/

四. 故障切换

slave 以 slaveof 命令完成主从配置, slave 是否只读由 slave-read-only 参数决定, 线上默认打开. 如上所述, master 或 slave 重启都会引起初始复制, 尽管很简单, 但是故障恢复过程没有 MySQL 等软件那么有效, 由此 redis 主从切换的过程如下, 当 master 出现故障时:

1. 切换 vip 到 slave;
2. 连接 slave 执行 slaveof no one, 断掉主从关系, 同时置 slave 为新的 master, 该命令执行后, 关闭 slave-read-only 参数, 允许应用更新;  
3. 关闭旧 master 的服务;

自动切换过程可以通过 redis sentinel 或 keepalived(vip) + scripts 方式完成: sentinel 是 redis 提供的高可用解决方案, 由一个或多个 sentinel 实例组成的系统可以监控任意多个 Server, 并完成下线或提升 slave 为 master 的操作, 过程类似于 MySQL Gelara 插件, 进行选举 quorum 再提升为 master 等, sentinel 中的 client-reconfig-script 和 client-reconfig-script 需要自定义完成;

keepalived + scripts 方式较为通用, 可以应用到较低版本, 也可以应用到多个一台主机的多个实例中. 应用连接 vip 进行读写, 在 master 出现故障的时候, keepalived 完成 vip 的切换, scripts 完成提升 slave 为 master 操作: keepalived配置中增加以下配置分别完成 keepalived 进入 master 和 backup 状态时需要执行的操作, 配置见 redis_ha.tar.gz

    notify_master "/home/redis/redis.sh -m"  # 提升为 master 
    notify_backup "/home/redis/redis.sh -s"  # 操作为 slave

redis脚本中 REDIS_MASTER_IP 指定为对端的实ip信息.比如:

10.0.21.5:6380 (master)
 +-- 10.0.21.7:6380 (slave)

则在21.5中, REDIS_MASTER_IP 设置为 10.0.21.7, 反之亦然.