复制
复制是高可用 Redis 的基础,哨兵和集群都是在复制的基础上实现高可用的。
Redis实例分为主节点和从节点,默认情况下都是主节点。每个从节点只有一个主节点,主节点可以同时具有多个从节点。复制的数据流是单向的,只能从主节点复制到从节点。
复制的相关配置
关键词:
slaveof <ip> <host>
;slaveof no one
;info replication
;masterauth
;repl-disable-tcp-nodelay
建立复制
一. 配置文件中加入 slaveof 配置:
1 | slaveof <masterip> <masterport> |
二. redis-server 启动命令加入 –slaveof:
1 | ./redis-server --port 7777 --slaveof 127.0.0.1 8888 |
三. 直接使用 slaveof 命令:
1 | 127.0.0.1:6380>slaveof 127.0.0.1 6379 |
slaveof本身是异步命令,执行slaveof命令时,节点只保存主节点信息后返回,后续复制流程在节点内部异步执行。主从节点复制成功建立后,可以使用
info replication
命令查看复制相关状态。当主节点通过设置requirepass参数进行密码验证时,从节点需要配置
masterauth
参数与主节点密码保持一致。传输延迟可以由
repl-disable-tcp-nodelay
决定,默认关闭,即主节点产生的任何命令数据都会及时发(适合同机房部署);开启则表示会合并较小TCP数据包从而节省宽带,默认发送间隔取决于Linux内核,一般默认40ms(适合复杂网络环境)。
断开复制
从节点执行
slaveof no one
断开与主节点复制关系,在断开与主节点的复制关系后,从节点晋升为主节点。原有数据也不会抛弃。slaveof 可以完成切主操作,即把当前从节点对主节点的复制切换为对另一个主节点。其流程是:切断与旧主节点的复制;建立与新主节点复制;删除从节点当前所有数据;对新主节点进行复制。【slaveof 命令务必小心执行。如果两个Redis实例数据本身不一样,但是B节点复制A节点时,B节点本身所有数据全部清空后再执行复制】
默认情况下,从节点使用slave-read-only=yes配置为只读模式,从节点的任何修改主节点都无法感知,建议线上不要修改从节点的只读模式。
当主节点自身shutdown了,从节点也不会升级为主节点,而是不断尝试。
1 | 52049:S 14 Mar 22:45:07.409 * Connecting to MASTER 127.0.0.1:6380 |
拓扑
一主一从
用于主节点宕机时从节点提供故障转移支持。
当应用写命令并发量较高且需持久化时,可以只在从节点上开启AOF,这样既保证数据安全性也避免了持久化对主节点的性能干扰。
当主节点关闭持久化功能时,如果主节点脱机要避免自动重启操作,从节点如果继续复制主节点会导致从节点数据也被清空。需在在从节点上执行 slaveof no one 断开与主节点的复制关系,再重启主节点。
一主多从
- 对于读占比较大的场景,可以把读命令发送到从节点来分担主节点压力。一些较耗时的读命令,如:keys、sort 等,可以在其中一台从节点上执行,防止慢查询对主节点造成阻塞从而影响线上服务的稳定性。
- 对于写并发量较高的场景,多个从节点会导致主节点写命令的多次发送从而过度消耗网络带宽,同时也加重了主节点的负载影响服务稳定性。
树状主从
该结构使从节点不但可以复制主节点数据,同时可以作为其他从节点的主节点继续向下层复制。
当主节点需要挂载多个从节点时为了避免对主节点的性能干扰,可以采用树状主从结构降低主节点负载和需要传送给从节点的数据量。
原理
总复制原理
从结点在执行完 slaveof 后正式开始复制过程:
- 保存主节点地址信息直接返回(ip host保存下来,但 master_link_status 状态为 down)
- 从节点内部的每秒运行的定时任务维护复制逻辑,定时任务发现新主节点后尝试建立网络连接,从节点会新建一个Socket套接字专门接收主节点复制命令。若无法建立连接,则无限重试至成功或执行 slaveof no one
- 连接建立成功后发送 PING,以检查套接字是否可用以及主节点是否可接受处理命令。若从节点无法接收PONG或超时,从节点会断开,等下次定时任务发起重连
- 权限校验,主节点若配置了 requirepass,需检查从节点的 masterauth。验证失败复制终止,从节点重新发起复制流程
- 同步数据集,首次会全量同步,后续会部分同步。
- 命令持续复制,主节点把当前数据同步给从节点后,后续会持续把写命令发送给从节点,保证主从一致性。
同步数据集
复制偏移量:
参与复制的主从都会为之自身复制偏移量,主节点在处理写入后会把命令字节长度做累加记录(info 下 master_repl_offset)。从节点会上报自身偏移量给主节点,所以主节点会保存从节点复制偏移量。从节点自身也会逐步累加记录自身的偏移量(info 下 slave_repl_offset)。
复制解压缓冲区:
是保存在主节点的一个固定长度的队列,默认1M,主节点响应写命令时,不但会把命令发送给从节点,也会写入复制积压缓冲区。主要用于部分复制及复制异常时的数据补救,参数为 info 下的 repl_backlog_*,缓冲区可用偏移量为 [repl_backlog_first_byte_offset,repl_backlog_first_byte_offset + repl_backlog_histlen]。
主节点运行ID:
每个Redis节点都有动态分配的40位十六进制字符串作为运行ID。从节点会保存主节点运行ID,每次关闭重启Redis,运行ID都会改变,改变后从节点需要重新做全量复制。不使用IP + HOST 进行记录的原因是:主节点重启变更整体数据集,此时再基于偏移量复制不安全。参数为 info 下 run_id。若不想改变可以使用 debug reload 重新加载RDB:redis-cli debug reload。
psync命令:
从节点使用 psync 命令完成全量复制及部分复制,格式为:psync {runId} {offset}
。主节点根据 psync 参数决定响应结果,回复+FULLRESYNC
从节点触发全量复制;回复+CONTINUE
,从节点将触发部分复制;回复+ERR
,说明主节点版本低于Redis2.8,无法识别psync,从节点将发送sync命令触发全量复制。
全量复制
第一次的全量复制成本较重,涉及到:
从节点发送 psync 命令,第一次的 offset 值为 1,主节点回复
+FULLRESYNC
。从节点保存主节点运行ID和偏移量,而主节点会执行 bgsave 保存 RDB 文件。
主节点发送RDB文件给从节点,从节点把接收的RDB文件保存在本地并直接作为从节点的数据文件。
RDB创建到传输完毕若超过60s,则全量复制失败,从节点清理已下载的临时文件。时间可以通过
repl-timeout
配置。Redis 2.8.18 后支持无盘复制,生成的RDB文件不保存到硬盘而是直接通过网络发送给从节点,通过repl-diskless-sync参数控制,默认关闭,适用于主节点所在机器磁盘性能较差但网络带宽较充裕的场景。
由于从节点接受 RDB 的时间段内,主节点依旧响应读写命令,等从节点加载完成后,主节点会把缓冲区内的数据发送给从节点,保证主从之间数据一致性。
期间若高流量写入造成主节点缓冲区溢出,也可造成全量复制失败。配置
client-output-buffer-limit slave 256mb 64mb 60
表示60秒内缓冲区消耗持续大于64MB或者直接超过256MB时则关闭复制连接。从节点清空本身数据,开始加载RDB。
从节点若开启了AOF,立刻执行 bgwriteaof,保证全量复制后 AOF 持久化文件立刻可以。
对于线上读写分离场景,如果此时从节点正出于全量复制阶段或者复制中断,那从节点在响应读命令可能拿到过期或错误的数据。配置中 slave-serve-stale-data参数默认开启状态,表示依然响应所有命令。对于无法容忍不一致的应用场景可置为no ,除info和slaveof命令其他所有命令只返回SYNC with master in progress
信息。
《Redis开发与运维》中的全量复制阶段示例图:
部分复制
是避免全量复制开销过大的优化措施。从节点正在复制主节点时出现网络闪断或命令丢失情况(超过repl-timeout
),等网络恢复后,由于从节点之前保存了自身已复制的偏移量和主节点的运行ID,从节点会向主节点要求补发丢失的命令(依然通过 psync )。主节点核对完运行ID后,直接从复制积压缓冲区找到偏移量之后的数据并发送。如果请求的偏移量不在主节点的积压缓冲区内,则无法提供给从节点数据,则部分复制会退化为全量复制。
心跳
复制连接建立后,主从都会有心跳检测:
主节点默认每隔10秒对从节点发送ping命令,判断从节点的存活性和连接状态(repl-ping-slave-period)
从节点在主线程中每隔1秒发送 replconf ack {offset} 命令,给主节点上报自身当前的复制偏移量
异步复制
主节点把写命令同步给从节点是异步发送完成的。异步造成的延迟可在主节点执行 info replication 查看。读写分离时,可以编写外部监控程序监听主从节点的复制偏移量,当延迟较大时触发报警或者通知客户端避免读取延迟过高的从节点。
Reference
《Redis开发与运维》