文章目录
- 1、Redis Replication 简介
- 2、Redis Replication 的特点
- 3、主节点关闭持久性时的复制安全性
- 4、Redis replication 要解决的问题
- 4.1、读写分离
- 4.2、高可用
- Redis replication 的工作原理
- 4.3、同步复制流程
- 4.3.1、保存主节点(master)信息
- 4.3.2、主从建立socket连接
- 4.3.3、发送 ping 命令
- 4.3.4、权限验证
- 4.3.5、同步数据集
- 4.3.6、命令持续复制
- 4.4、数据同步
- 4.4.1、psync 命令
- 4.4.1.1、psync 命令依赖的组件
- 4.4.1.1.1、复制偏移量
- 4.4.1.1.2、复制积压缓冲区
- 4.4.1.1.3、主节点运行 id
- 4.4.1.2.psync 命令运行流程
- 4.4.2、全量复制
- 4.4.2.1、全量复制流程
- 4.4.2.1、全量复制时间开销
- 4.4.3、部分复制
- 4.7、异步复制
1、Redis Replication 简介
Redis replication 是一种 master-slave 模式的复制机制,这种机制使得 slave 节点可以成为与 master 节点完全相同的副本
当 slave 和 master 之间的连接断开时, slave 会自动重连 master,无论这期间 master 发生了什么, slave 都将尝试让自身成为 master 的精确副本。
Redis Replication 的实现依靠三个主要的机制:
1、当一个 master 实例和一个 slave 实例连接正常时, master 会发送一连串的命令流来保持对 slave 的更新,以便于将自身(master)数据集的改变复制给 slave 。包括客户端的写入、key 的过期或master库的其它改变动作。
2、在Redis2.6之前,主从断开重连后,会进行一次快照操作(rdb)然后将快照发送给从数据库,即使断开期间只有几条命令被执行,这就使得断开重联后的数据恢复过程效率很低。Redis2.8之后,当 master 和 slave 之间的连接断开之后(因为网络问题、主库或从库检测到超时), slave 会重新连接上 master 并尝试进行"部分重同步"也叫增量复制(partial resynchronization),用于获取在断开连接期间内丢失的命令流。
3、当无法进行部分重同步时,slave 会请求进行"完整同步"(full resynchronization)。这会涉及到一个更复杂的过程,例如 master 需要创建所有数据的快照,将之发送给 slave ,之后在master数据集更改时持续发送命令流到 slave。也就是2.8后有了全部同步和部分重同步两种模式,而2.6之前只有全部同步模式。
2、Redis Replication 的特点
Redis使用默认的异步复制,具有低延迟和高性能,是绝大多数Redis使用场景的默认模式。从库会异步的确认与主库定期接收到的数据量。主库不会每次去等待从库处理完命令。
客户端可以使用WAIT命令请求某些数据的同步复制。 但是,WAIT只能确保在其他Redis实例中存在指定数量的已确认副本,但它不会将一组Redis实例转换为具有强一致性的CP系统:在故障转移期间,确认的写入仍然可能丢失,具体取决于 关于Redis持久性的确切配置。 然而,对于WAIT,在故障事件之后丢失写入的概率大大降低到某些难以触发的故障模式。
以下时 Redis replication 的重要特点:
1)、 Redis使用异步复制,slave 和 master 之间异步地确认处理的数据量。
2)、 一个 master 可以拥有多个 slave。
3)、 slave 可以接受其他 slave 的连接。除了多个 slave 可以连接到同一个 master 之外, slave 之间也可以像级联结构(cascading-like structure)连接到其他 slave 。自Redis 4.0 版本起所有的 sub-slave 将会从 master 收到完全一样的复制流。
4)、 Redis复制在 master 端是非阻塞的。 这意味着当一个或多个 slave 执行初始同步或部分重新同步时,主服务器可以继续处理查询。
5)、 复制在 slave 端也是非阻塞的。当 slave 进行初次同步时,它可以使用旧数据集处理查询请求,前提时你在redis.conf中配置了让Redis这样做的话。否则,您可以将 Redis 从属配置为在复制流关闭时向客户端返回错误。 但是,在初始同步之后,必须删除旧数据集并且必须加载新数据集。 salve 将在此简短窗口期间阻止传入连接(对于非常大的数据集,可能长达数秒)。 从Redis 4.0开始,可以配置Redis,以便删除旧数据集在不同的线程中发生,但是加载新的初始数据集仍然会在主线程中发生并阻塞从属。
6)、 复制既可以被用在可伸缩性,以便只读查询可以有多个 slave 进行(例如 O(N) 复杂度的慢操作可以被下放到 slave ),或者仅用于提高数据安全性和高可用性。
7)、 可以使用复制来避免 master 将全部数据集写入磁盘造成的开销,一种典型的应用时配置 master 的 Redis.conf 以避免持久化到磁盘,然后在 slave 配置为不定期保存或是启用 AOF。但是,这个设置必须小心处理,因为重新启动 master 程序将找不到持久化文件,会清空原有的数据,如果 slave 试图与它同步,那么这个 slave 也会被清空。
3、主节点关闭持久性时的复制安全性
在使用Redis复制的设置中,强烈建议在主服务器和从服务器中启用持久性。 如果无法做到这一点,例如由于磁盘速度很慢而导致延迟问题,则应配置实例以避免重启后自动重启。
为了更好地理解为什么关闭持久性的主服务器配置为自动重启是危险的,请查看以下故障模式,其中主服务器及其所有从服务器数据将都被清空:
- 节点A充当主节点,持久性关闭,节点B和C从节点A复制。
- 节点A崩溃,但它自动重启系统,由于关闭了持久性,节点将以空数据集重新启动。
- 节点B和C将从节点A复制,因为A是空的,因此节点B和C数据副本也会被清除。
4、Redis replication 要解决的问题
根据 Redis replication 特点的第六条,我们可以归纳为replication 主要用于解决两个问题:
4.1、读写分离
一个 master 用于写,多个 slave 用于分摊读的压力。
4.2、高可用
如果 master 挂掉了,可以提升(promote)一个 slave 为新的 master,进而实现故障转移(failover)。
Redis replication 的工作原理
4.3、同步复制流程
在从节点执行 slaveof 命令后,复制过程便开始了,大致分为 6 个过程:
执行 slaveof 后 Redis 会 打印 如下 日志: 通过 该 日志 可以 帮助 运 维 人员 定位 发送 slaveof 命令 的 客户 端, 方便 追踪 和 发现问题。
4.3.1、保存主节点(master)信息
执行 slaveof 后从节点只保存主节点的地址信息就直接返回了,这时复制流程还没真正开始,在从节点 6380 执行 info replication 可以看到如下信息:
master_host:127.0.0.1
master_port:6379
master_link_status:down
从统计信息中可以看到,主节点的 ip 和 port 都被记录下来了,但是主节点的状态(master_link_status)是下线状态。
执行 slaveof 命令后,redis 会打印如下日志:
SLAVE OF 127. 0. 0. 1: 6379 enabled
(user request from 'id= 65 addr=127.0.0.1:58090 fd=5 name= age=11 idle=0
flags= N db= 0 sub= 0 psub= 0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=slaveof')
通过该日志我们可以定位发送 slaveof 命令的客户端。
4.3.2、主从建立socket连接
从节点(slave)内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主 节点后,会尝试与该节点建立网络连接
从节点会建立一个 socket 套接字,专门用于接受主节点发送的复制命令。 从节点连接成功 后会打印如下日志:
* Connecting to MASTER 127. 0. 0. 1: 6379
* MASTER <-> SLAVE sync started
如果从节点无法建立连接,定时任务会无限重试直到连接成功或者执行 slaveof no one 取消 复制。
关于连接失败,可以在从节点执行 info replication 查看 master_link_down_since_seconds 指标,它会记录与主节点连接失败的系统时间。从节点连接主节点失败时也会每秒打印如下日志:
# Error condition on socket for SYNC: {socket_ error_ reason}
4.3.3、发送 ping 命令
连接建立成功后,从节点发送 ping 请求进行首次通信,ping 请求主要目的如下:
- 检查主从之间网络套接字是否可用
- 检查主节点当前是否可接受处理命令
如果从节点没有收到主节点的 pong 回复或者超时,比如网络超时或者主节点正在阻塞无法响应命令,从节点会断开复制连接,下次定时任务再发起重连。
从节点发送的 ping 命令成功返回后,redis 会打印如下日志,并继续后续复制流程:
Master replied to PING, replication can continue...
4.3.4、权限验证
如果主节点设置了 requirepass 参数, 则需要密码验证,从节点必须配置 masterauth 参数 保证与主节点相同的密码才能通过验证;如果验证失败复制将终止,从节点重新发起复制流程。
4.3.5、同步数据集
主从复制连接正常通信后,对于首次建立复制的场景,主节点会把持有的数据全部发送给从节点,这部分操作是耗时最长的步骤。Redis 在2.8 版本以后采用新复制命令 psync 进行数据同步,原来的 sync 命令依然支持,保证新旧版本的兼容性。 新版同步划分两种情况: 全量同步和部分同步
4.3.6、命令持续复制
当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。 接下来主节点会持续地把 写命令发送给从节点,保证主从数据一致性。
4.4、数据同步
Redis 在2.8及以上版本使用 psync 命令完成主从数据同步,同步过程分为:全量复制和部分复制。
- 全量复制,一般用于首次复制场景,Redis 早期支持的复制功能只有全量复制,它会把主节点全部数据一次性发送给从节点,当数据量较大时,会对主从节点和网络带来很大的开销。
- 部分复制,用于处理在主从复制中因网络抖动等原因造成的数据丢失场景,当从节点再次连上主节点后,如果条件允许,主节点会补发丢失数据给从节点。因为补发的数据远远小于全量数据,可以有效的避免全量复制的过高开销。
4.4.1、psync 命令
4.4.1.1、psync 命令依赖的组件
psync 命令运行需要以下组件支持:
- 主从节点各自复制偏移量。
- 主节点复制积压缓冲区。
- 主节点运行 id。
4.4.1.1.1、复制偏移量
参与主从复制的主从节点都会维护自身复制的偏移量。主节点(master)在处理完写入命令后,会把命令的字节长度做累加记录,统计信息在 info relication 中的 master_repl_offset 指标中:
127.0.0.1:6379> info replication
# Replication
role:master
...
master_repl_offset:1055130
从节点(slave)每秒中上报自身的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移量,统计指标如下:
127.0.0.1:6379> info replication
connected_slaves:1
slave0:ip= 127.0.0.1,port=6380, state=online, offset= 1055214, lag=1
...
从节点在接收到主节点发送的命令后,也会累加记录自身的偏移量。统计信息在 info replication 中的 slave_repl_offset 指标中:
127.1.1.1:6380>info replication
#Replication
role:slave
....
slave_repl_offset:1055214
通过对比主从节点的复制偏移量,可以判断主从节点数据是否一致
4.4.1.1.2、复制积压缓冲区
复制积压缓冲区是保存在主节点上的一个固定长度的队列,默认大小为 1MB,当主节点有连接的从节点(slave)时被创建,这时主节点(master) 响应写命令时,不但会把命令发送给从节点,还会写入复制积压缓冲区。
由于缓冲区本质上是先进先出的定长队列,所以能实现保存最近已复制数据的功能,用于部分复制和复制命令丢失的数据补救。
复制缓冲区相关统计信息保存在主节点的 info replication 中:
127.0.0.1:6379> info replication
# Replication
role:master
...
repl_backlog_ctive:1 //开启复制缓冲区
repl_backlog_size:1048576 // 缓冲区最大长度
repl_backlog_first_byte_offset:7479 // 起始偏移量,计算当前缓冲区可用范围
repl_backlog_histlen:1048576 //已保存数据的有效长度。
根据统计指标,可算出复制积压缓冲区内的可用偏移量范围:[repl_backlog_first_byte_offset,repl_backlog_first_byte_offset + repl_backlog_histlen]。
4.4.1.1.3、主节点运行 id
每个 Redis 节点启动后都会动态分配一个40位的十六进制字符串作为运行ID。运行ID的主要作用是用来唯一识别Redis节点,比如从节点保存主节点的运行ID识别自己正在复制的是哪个主节点。
如果只使用 ip+port 的识别主节点,当主节点重启变更了整体数据集时,从节点再基于偏移量复制数据将时不安全的,因此当运行ID变化后从节点讲座全量复制。
可以使用 info server 命令查看当前节点的运行ID:
127.0.0.1:6379> info server
# Server
redis_version:5.0.4
...
run_id:545f7c76183d0798a327591395b030000ee6def9
4.4.1.2.psync 命令运行流程
从 节点 使用 psync 命令 完成 部分 复制 和 全 量 复制 功能, 命令 格式: psync{ runId}{ offset}, 参数 含义 如下:
- runId:从节点所复制主节点的运行id
- offset:当前从节点已复制的数据偏移量
psync 命令的运行流程如下:
1、从节点(slave)发送 psync 命令给主节点,参数 runId 是当前从节点保存的主节点运行ID;参数 offset 是当前从节点保存的复制偏移量,如果是首次参与复制,则默认值为 -1。
2、主节点(master)根据 psync 参数和自身数据情况决定响应结果:
- 如果回复 + FULLRESYNC{ runId}{ offset},从节点将触发全量复制
- 如果回复 + CONTINUE,从节点将触发部分复制流程
- 如果回复 + ERR,说明主节点版本低于 2.8,无法识别 Psync 命令,从节点将发送旧版的 sync 命令触发全量复制流程
4.4.2、全量复制
4.4.2.1、全量复制流程
1、发送 psync 命令进行数据同步,由于是第一次进行复制,从节点没有复制偏移量和主节点的运行 ID,所以发送 psync-1。
2、主节点根据 psync-1 解析出当前为全量复制,回复 +FULLRESYNC 响应。
3、从节点接收到主节点的响应数据保存运行 ID 和偏移量 offset。
4、主节点执行 bgsave 保存 RDB 文件到本地。
5、主节点发送 RDB 文件给从节点,从节点把接收的 RDB 文件保存在本地并直接作为从节点的数据文件。
6、对于从节点开始接收 RDB 快照到接收完成期间,主节点仍然响应读写命令,因此主节点会把这期间写命令数据保存在复制客户端缓冲区内,当从节点加载完RDB文件后,主节点再把缓冲区内的数据发送给从节点,保证主从之间数据 一致性。 如果主节点创建和传输 RDB 的时间过长,对于高流量写入场景非常容易造成主节点复制客户端缓冲区溢出。 默认配置为 client-output-buffer-limit replica 256mb 64mb 60, 如果 60 秒内缓冲区消耗持续大于 64MB 或者直接超过256MB 时,主节点将直接关闭复制客户端连接,造 成全 量 同步 失败。
7、从节点接收完主节点传送来的全部数据后会清空自身旧数据。
8、从节点清空数据后开始加载 RDB 文件,对于较大的 RDB 文件,这一步操作依然比较耗时。
4.4.2.1、全量复制时间开销
通过分析全量复制的所有流程, 我们会发现全量复制是一个非常耗时的操作,它的时间开销主要包括以下几点:
- 主节点 bgsave 时间。
- RDB 文件网络传输时间。
- 从节点清空数据时间。
- 从节 加载 RDB 的时间。
- 可能的 AOF 重写时间。
因此当数据量达到一定规模之后,由于全量复制过程会进行多次持久化操作和网络传输数据,这期间将会消耗大量主从节点服务器的CPU、内存和网络资源。一般除了第一次复制时采用全量复制之外,对于其他场景应该避免全量复制的发生。正因为全量复制存在该问题,redis 又提供了部分复制的功能。
4.4.3、部分复制
部分复制主要是 redis 针对全量复制的开销过高做出的一种优化措施,使用 psync {runId} {offset} 命令来实现部分复制。
当从节点正在复制主节点时,如果出现网络闪断等异常情况时,从节点会向主节点要求补发丢失的数据,如果主节点的复制积压缓冲区存在这部分数据则直接发送给从节点,这样就可以保持主从节点复制的一致性。补发的数据一般都比较小,所以开销会很小。
1、当主从节点之间网络出现中断时,如果超过 repl-timeout 时间,主节点会认为从节点故障并中断复制连接。
2、主从连接中断期间主节点依然响应命令,这些命令主节点内部会保存在复制积压缓冲区,默认最大缓存 1MB。
3、当主从节点网络恢复后,从节点会再次连上主节点
4、由于从节点之前保存了自身已复制的偏移量和主节点的运行ID。因此会把它们当作 psync 参数发送给主节点,要求进行部分复制操作。
5、主节点接到 psync 命令后首先核对参数 runId 是否与自身一致,如果一致,根据参数 offset 在自身复制积压缓冲区查找,如果偏移量之后的数据存在缓冲区中,则对从节点发送+CONTINUE 响应,表示可以进行部分复制。
4.7、异步复制
主节点不但负责数据读写,还负责把写命令同步给从节点。 写命令的发送过程是异步完成,也就是说主节点自身处理 完写命令后直接返回给客户端,并不等待从节点复制完成。
由于主从复制过程是异步的,就会造成从节点的数据相对主节点存在延迟。具体延迟多少字节,我们可以在主节点执行 info replication 命令 查看 相关 指标 获得。
slave0:ip= 127.0.0.1, port=6380, state=online, offset=841, lag=1 master_repl_offset:841
在统计信息中可以看到从节点 slave0 信息, 分别记录了从节点的 ip 和 port,从节点的状态,offset 表示当前从 节点的复制偏移量,master_repl_offset 表示当前主节点的复制偏移量,两者的差值就是当前从节点复制延迟量。
参考资料:
《Redis 开发与运维》
https://redis.io/topics/replication