主从复制,指将一台 Redis 服务器的数据,复制到其他的 Redis 服务器。前者称为主节点(Master),后者称为从节点(Slave);数据的复制是单向的,只能由主节点到从节点。默认情况下,每台 Redis 服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。
Redis 引入主从复制功能有几个重要原因:
启动一个主节点
redis-server -p 6379
启动一个从节点以并配置其作为主节点的从节点
redis-server --port 6380 --slaveof 127.0.0.1 6379
然后启动两台节点的客户端
redis-cli -p 6379
redis-cli -p 6380
使用下面的命令从两个实例中获取 Replication 节的相关信息。
INFO replication
然后在主节点设置一个键,在从节点是可读的。从节点是只读的,如果在从节点修改键值会报错
可以通过配置 slave-read-only 为 no 以使从节点可写,但是这没什么意义,因为写入从节点不会复制到集群中的任何节点,而且会被主节点的下一次修改覆盖。
Redis 的主从复制是通过一种异步复制的机制来实现的,基本流程:
SYNC
命令,请求进行一次完整的同步。SYNC
命令后,开始生成一个快照(RDB 文件)并将该快照保存到磁盘。同时,主服务器将新的写命令(写操作)缓存到内存缓冲区。总体而言,主从复制是通过快照和增量复制的结合来实现的。这种异步复制机制使得从服务器可以在主服务器的基础上保持实时同步,提高了系统的可用性和可靠性。
使用 netcat 模拟从节点与主节点进行通信。向主节点发送一个 SYNC 开始同步,主节点返回快照文件和缓存的命令
从节点之后会将收到的内容写入硬盘中的临时文件中并替换目录下的 RDB 快照文件,之后的过程就是 redis 持久化恢复的过程了。最后将主节点缓冲区中的写命令发送给从服务器完成复制初始化。
redis 在同步的过程中不会阻塞,可以继续处理来自客户端的请求,但是会使用同步前的数据进行响应。可以配置 slave-serve-stable-data 参数为 no 来使数据库在同步完成前对所有命令都回复错误(除了 INFO 和 SLAVEOF)。
完成复制初始化后,进入持续同步阶段,主节点中任何导致数据变化的命令都会发送到从节点。使用 RESP 协议进行通信。在主节点执行命令 SET key1 value
,在模拟从节点的 nc 客户端可以收到如下格式的消息。
在没有增量复制时,主服务器和从服务器之间会保持心跳检测的连接。如果从服务器在一定时间内没有收到主服务器的心跳或数据包,它将尝试重新连接或请求重新同步。可以看到 nc 客户端每隔一段时间收到了来自主节点的 ping
redis 采取了乐观复制的策略,即容忍在一段时间内主从节点的数据是不同步的,但是两者最终都会同步。也就是说,redis 的主从节点的复制过程本身是异步的,这一特性保证了主节点的性能。 但是,当主节点将新的命令传递给从节点之前,双方的网络连接断开了,此时二者的数据就会是不一致的。从这个角度来看,主节点是无法知道这个命令的结果是最终同步给了多少个数据库的,这样可能降低数据的冗余存储程度。 不过 redis 提供了两项配置。
min-slave-to-write
:
例如:
min-slave-to-write 3
min-slave-max-lag
:
例如: min-slave-max-lag 10
主从复制可以实现读写分离,以提高服务器的负载能力。在一些生产环境中,对数据库的读频率远远大于写,单个 redis 服务器无法处理这么庞大的请求,可以复制多个从节点分担读请求,主节点只复制写请求。所有的读操作则可以分散到多个从节点。可以通过在应用程序中配置连接到从节点的读连接来实现。这样可以减轻主节点的负担,提高整体系统的读取性能。例如,在应用程序的 Redis 连接池配置中,可以将读操作的连接指向多个从节点,实现读写分离。
在主从复制架构中,通常会使用代理或专门的负载均衡器,将请求分发到不同的 Redis 节点。这可以是基于软件的负载均衡器(如 HAProxy、Nginx)或者专门用于 Redis 的代理(如 Twemproxy、Redis Sentinel)。
为了提高性能,可以通过复制功能创建多个从节点,并在从节点启用持久化,在主节点禁用持久化。从节点崩溃重启后可以自动从主节点中将数据同步过来,所以无需担心数据丢失。
但是当主节点崩溃时,情况就比较复杂了,需要先将一个从节点作为主节点,然后再将崩溃的原主节点作为从节点来恢复数据。从当前的从节点中选择一个节点。这个从节点应该是与主节点数据最接近的,同时复制延迟最小的一个。使用 SLAVEOF NO ONE
命令将其从从节点升级为主节点,然后启动之前崩溃的主数据库,使用 SLAVEOF
将其设置为新的主节点的从节点即可同步数据。
当开启复制且关闭主节点的持久化时,一定不要使用一些管理工具令主节点崩溃后自动重启,这样启动后主节点数据不但不会恢复,还会因为复制而导致从节点拥有的数据全部清空。
无论哪种情况,手工维护从数据库或者主数据库的重启以及数据恢复都很麻烦,redis 提供了哨兵机制来自动化实现这一过程,避免手工维护可能出现的问题。这里就不介绍了,之后再总结。
传统的 Redis 复制过程中,主节点会将数据写入磁盘,并将数据传输给从节点进行复制。当主节点禁用持久化时,如果执行了复制初始化操作,redis 依然会生成 RDB 文件,所以下次启动后主节点会以该文件恢复数据。由于复制初始化操作时间不确定,因此可能会导致恢复的数据不完整。同时,每次和主节点同步时都需要执行一次快照,要磁盘中进行 IO 操作,可能会导致性能瓶颈。
redis 引入了无盘复制选项。在无盘复制中,主节点在复制过程中不需要将数据写入磁盘,而是直接将数据通过网络传输给从节点进行复制。这样可以减少磁盘 IO 的开销,提高复制的效率。
可以配置项 repl-diskless-sync yes
开启该功能。
当从节点从主节点断开后,重连后从节点会发送 SYNC 命令来重新进行一次完整的复制操作,即使断开后的数据改变很小,也需要将完整的数据快照传输一份,这种方式显然不够理想。
redis 引入了主从断线重连情况下的增量复制。
增量复制机制基于以下三点
当主从节点连接后,不再发送 SYNC 来同步,而是发送 PSYNC [主节点的 run id] [断开前最新的命令偏移量]这样格式的命令。主节点会执行下面的判断来确定是执行增量复制还是完整的复制操作:
<run_id>
是否与当前主节点的 Run ID 相同。如果不同,可能意味着主节点已经发生了切换,需要进行全量同步。<run_id>
相同,则主节点进一步判断 <offset>
是否在主节点的队列中。<offset>
在队列中,主节点会执行增量复制,将从 <offset>
位置开始的命令发送给从节点,从而实现增量同步。<offset>
不在队列中,或者其他判断条件不满足,可能会导致主节点拒绝增量同步请求,需要执行全量同步。命令队列即 Redis 的复制积压缓冲区(replication backlog)是一个固定长度的循环队列,用于保存主节点的写操作记录,以便在从节点断线后,重新连接时提供增量复制所需的数据。
repl-backlog-size
:
repl-backlog-size
配置选项,可以指定积压队列的大小,例如,设置为 repl-backlog-size 10mb
将队列大小增加到 10MB。repl-backlog-ttl
:
repl-backlog-ttl
配置选项用于指定释放周期,单位是秒。例如,repl-backlog-ttl 3600
表示每小时释放一次积压缓冲区中的数据。------本页内容已结束,喜欢请分享------