Redis是一个开源的,遵守BSD许可协议的key/value缓存系统,并由其高效的响应速度以及丰富的数据结构而闻名。Redis在京东的使用也是非常普遍的,包括很多关键业务上的使用,由于Redis官方集群还未发布,在使用Redis的过程中需要面对Redis的单点问题,京东采用的是一种比较通用的解决方案即由主从备份再加相应的主从切换(在一些场景下可能进行读写分离),使主Redis出现失效的时候可以快速的切换到从Redis上。但Redis目前存在的一个问题是主从复制在遇到网络不稳定的情况下,Slave和Master断开(包括闪断)会导致Master需要将内存中的数据全部重新生成rdb文件(快照文件),然后传输给Slave。Slave接收完Master传递过来的rdb文件以后会将自身的内存清空,把rdb文件重新加载到内存中。这种方式效率比较低下,尤其是在数据量大的情况下,毕竟网络闪断未必丢数据或者说丢的数据只是少部分,但却要为此付出将整个内存数据都重新传输一次的代价。如果能够将闪断过程的更新数据传递给Slave,那么就不需要将Master内存中的所有数据都传递给Slave了。Redis作者在2.8的候选版(以下简称Redis2.8)中已经将这个部分复制的思路实现了。
那么Redis2.4.16的全量复制与Redis2.8的部分复制是如何实现的呢?如下图所示,这5个状态是Slave在主从复制过程涉及到的几个状态,其中REDIS_REPL_NONE是Redis启动时候默认的状态。图1-2所示的四个状态表示站在Master的角度来看,Slave所处于的状态,因为Slave在Master端看来就是一个特殊的client(同理Master在Slave端看来也是一个特殊的client)。
/* Slave replication state – Slave side */
#define REDIS_REPL_NONE 0 /* No active replication */
#define REDIS_REPL_CONNECT 1 /* Must connect to Master */
#define REDIS_REPL_CONNECTING 2 /* Connecting to Master */
#define REDIS_REPL_TRANSFER 3 /* Receiving .rdb from Master */
#define REDIS_REPL_CONNECTED 4 /* Connected to Master */
Slave自身的状态
#define REDIS_REPL_WAIT_BGSAVE_START 3 /* Master waits bgsave to start feeding it */
#define REDIS_REPL_WAIT_BGSAVE_END 4 /* Master waits bgsave to start bulk DB transmission */
#define REDIS_REPL_SEND_BULK 5 /* Master is sending the bulk DB */
#define REDIS_REPL_ONLINE 6 /* bulk DB already transmitted, receive updates */
Master端的Slave状态
Redis在接收到“slaveof ip port”命令以后,首先会将自身的状态置为REDIS_REPL_CONNECT,表示需要与自己的Master连接,此时Slave并没有与Master做连接。Redis每隔100ms会调用serverCron()函数一次,每10次serverCron()的调用会调用replicationCron()一次,即每1s会调用一次replication()函数。在replication()函数中,会检查Slave的状态,如果是处于REDIS_REPL_CONNECT状态,就会建立syncWithMaster()的事件处理函数,并将Slave的状态改成REDIS_REPL_CONNECTING。syncWithMaster()函数主要是向Master发送sync命令,当该事件处理函数被触发以后会将Slave的状态改成REDIS_REPL_TRANSFER,表示Slave已经准备就绪要接收Master生成的rdb文件。
回到Master的角色,Master发现有一个Slave连接上来,如果此时的Master一个Slave都没有且没有后台快照进程,则启动一个后台进程将当前内存中的数据生成一个rdb文件,同时将Slave的状态置为REDIS_REPL_WAIT_BGSAVE_END状态,表示该Slave等待Master的快照进程结束。在后台进行生成rdb文件的时候,如果有对redis的更新命令,Master会将这些更新命令存到该Slave的buffer中,如果buffer满了会另外开辟list来存储这些更新命令。当后台快照进程结束,Master会将该Slave的状态改为REDIS_REPL_SEND_BULK,同时注册sendBulkToSlave()事件处理函数用于将生成的rdb文件传输给Slave。等rdb传输结束以后,sendBulkToSlave()事件函数会被删除,Slave的状态会被更改为REDIS_REPL_ONLINE,另外再注册sendReplyToClient()事件函数,将Master在快照内过程中的所有更新操作(Slave的buffer里存的命令)发给Slave。
再回到Slave的角色,当Master向Slave传输完rdb文件以后,Slave自身会将状态改为REDIS_REPL_CONNECTED,表示复制已完成,处于与Master保持实时同步的状态。
上述描述的状态转换如图1-3所示,由图中可知,站在Slave角色看,当出现网络中断的时候不管Slave本身是处于REDIS_REPL_CONNECTING、REDIS_REPL_REPL_TRANSFER还是REDIS_REPL_CONNECTED,都会调用相应的处理函数使Slave进入REDIS_REPL_CONNECT状态,这就意味着Slave需要重新向Master发送sync命令,重新进行一次全量同步过程。图中的REDIS_REPL_WAIT_BGSAVE_START状态是在Slave连接上Master的时候(站在Master的角色看),当时Master刚好后台有快照进程且该快照进程生成的rdb不适合直接传给该Slave时出现的状态,则将Slave的状态置为REDIS_REPL_WAIT_BGSAVE_START。如果此时有快照进程且找到了另外的发起快照进程的Slave,只需要将另外的Slave的buffer内容拷贝到该Slave的buffer中,然后直接进入REDIS_REPL_WAIT_BGSAVE_END状态。如果此时没有后台快照进程,Slave直接进入REDIS_REPL_WAIT_BGSAVE_END状态,同时启动一个后台快照进程。
图1:Redis-2.4.16主从复制状态转换图
在上述状态转图中存在的最大问题在于任何网络闪断都会导致Slave与Master重连,然后重新进入快照过程,需要花费较长的时间重新传输rdb文件,而Slave在接收完rdb文件以后试图将rdb文件恢复到内存的过程中是不能服务的(除info命令外)。所以提供部分复制至少可以做到在网络闪断且更新命令不太多的情景下能够尽量的避免全量复制的方案就显得尤为重要。
庆幸的是Redis2.8中里已经能够做到在网络闪断的情况下,Slave重新连接上Master以后,仅仅只传输闪断期间的更新命令。在Redis2.8中redisServer结构中增加了一个成员:
char runid[REDIS_RUN_ID_SIZE+1]; /* ID always different at every exec. */
该runid是由一个getRandomHexChars()函数生成的每次不同的一个唯一标识,不同Redis实例之间该runid是不同的,同一个Redis重启以后,其runid和之前的runid也是不同的。
还增加了比较重要的几项数据成员,如下所示:
char *repl_backlog; /* Replication backlog for partial syncs */
long long repl_backlog_size; /* Backlog circular buffer size */
long long repl_backlog_histlen; /* Backlog actual data length */
long long repl_backlog_idx; /* Backlog circular buffer current offset */
long long repl_backlog_off; /* Replication offset of first byte in the backlog buffer. */
time_t repl_backlog_time_limit; /* Time without Slaves after the backlog gets released. */
time_t repl_no_Slaves_since; /* We have no Slaves since that time.
Only valid if server.Slaves len is 0. */
Redis2.8增加的数据成员
repl_backlog是redis用于存储更新命令的一块buffer,在部分复制的时候Slave会请求Master从这块buffer中获取闪断情况下丢失的更新操作。repl_backlog在redis启动的时候初始化为NULL,当有Slave连接上来的时候,会被指向创建的buffer,默认为1024*1024(即1Mb)。repl_backlog_size表示该buffer的大小(默认1024*1024,即1Mb)。该buffer是作为一个环形缓存区使用的,当有数据超过buffer的大小以后就会重新从buffer的头部开始写入。repl_backlog_idx表示当前缓存数据的尾部(因为是环形buffer)。repl_backlog_off是全局缓存的偏移量,从开始缓存数据起一直在增长。如果Master一个Slave都没有,则超过一段时间以后repl_backlog会被释放,默认超时时间是1小时。
Redis2.8的主从复制如图1-5所示,Slave如果与Master的连接超时了,Slave会将调用freeClient(server.Master)把连接关闭。该freeClient()函数与2.4版本的相比做了改动,会将Master对应的数据结构的一些信息存起来作为cache Master,其中后续被用于部分复制的最重要的两个信息一个是Master runid,另一个是reploff。reploff是Slave端接收到Master端传递过来的命令以后不断更新记录的全局偏移量的值,该值和Master端的repl_backlog_off对应,正常情况下reploff<=repl_backlog_off。如果Slave尝试部分复制失败以后,就会将该cache Master释放。
Redis2.8中主从复制的过程增加了REDIS_RECIVE_PONG状态,该状态作为试图与Master同步的时候先ping一下的一个中间状态。当ping通以后,Slave首先会尝试部分复制,从cache Master中拿出Master runid和reploff传给Master,表示请求部分复制。第一次的时候,由于Slave端的cache Master是NULL,所以Slave向Master发送的runid是“?”,偏移量是“-1”,当Master收到这两个变量以后会将自身的runid和实际偏移量发送给Slave,同时让Slave发起一次全量同步。
Slave与Master完全同步以后,maste的更新命令会被存到repl_backlog中,同时不断更新偏移量等相关变量。这些更新命令不断地被发送到Slave端,Slave也随之更改自己记录的偏移量。当期间再次有网络断开的情况,Slave会根据记录的runid和reploff向Master请求部分复制,Master检查Slave请求的偏移量对应的内容是否还在repl_backlog中,即比较repl_backlog_off和Slave传递过来的reploff的值的差是否小于等于repl_backlog中实际数据的长度,如果满足条件则将这部分内容发送给Slave,部分复制完成。否则让Slave进行全量复制。
Redis2.8之前的版本没有提供部分复制功能,当出现网络闪断的情况会导致主从之间的全量复制。Redis2.8增加了部分复制功能,在处理网络闪断的情况下是非常有效的,这也是出Redis集群之前需要提供的基本保证。默认1Mb的repl_backlog在访问量大的情况下可能效果未必理想,这个可以通过更改配置文件中的repl-backlog-size的值实现repl_backlog的大小的调整。还有repl_backlog在没有Slave的情况下过多久再释放的时间阈值也可以通过配置文件中的repl-backlog-ttl进行调整。
来源:京东
相关推荐
### Redis主从复制和集群配置详解 #### 一、Redis主从复制原理及配置 **1.1 概述** Redis的主从复制机制是一种重要的数据同步方式,它支持一个或多个从数据库(slave)与主数据库(master)之间进行数据同步。其中,...
Redis 主从复制技术原理 概述 Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。Redis 主从复制是指将一台 Redis 服务器的数据,复制到...
LAMP架构+Redis主从复制 本文档主要介绍了LAMP架构的安装和配置,以及Redis主从复制的设置。LAMP架构是指Linux操作系统、Apache HTTP服务器、MySQL数据库和PHP编程语言的组合,而Redis则是一个开源的、基于内存的...
### Redis 主从复制集群知识点详解 #### 一、Redis 主从复制概述 Redis 主从复制是一种数据冗余机制,主要用于实现读写分离、负载均衡以及数据备份等目的。通过搭建主从复制集群,可以有效避免单点故障问题,并且...
1、生成恶意.so文件,下载RedisModules-ExecuteCommand使用make编译即可生成 1、监听本地1234端口 2、将Redis服务器设置
描述redis主从复制的过程中的各种细节,包括各个阶段所使用的事件处理函数
Redis 主从复制和哨兵配置手册 Redis 主从复制是一种高可用性解决方案,可以确保数据的安全和可靠性。在这个手册中,我们将详细介绍 Redis 主从复制的配置和哨兵的配置。 环境准备 在开始配置 Redis 主从复制之前...
下面是对 Redis 的常用命令、配置文件、持久化、事务、主从复制、Jedis 使用的详细讲解。 Redis 常用命令 Redis 提供了很多有用的命令来管理和操作数据。下面是一些常用的 Redis 命令: * SET key value:设置...
当 Master 恢复正常时,从 Slave 同步数据,同步数据之后关闭主从复制功能,恢复 Master 身份,于此同时 Slave 等待 Master 同步数据完成之后,恢复 Slave 身份。 2. Keepalived 介绍: Keepalived 是一个基于 ...
1.使用 DICT 协议添加一条测试记录 2.设置保存路径 3.设置保存文件名 4.保存 1.连接远程主服务器 2.设置保存路径 3.设置保存文件名 4.保存
windows下redis主从复制。一个master,两个slave。附带博客介绍https://blog.csdn.net/xixiyuguang/article/details/105121660
本文将深入探讨Redis主从复制的机制,包括全同步和部分同步的过程,以及积压空间在主从复制中的作用。 在Redis主从复制中,有两种同步方式:全同步和部分同步。全同步发生在主从服务器初次建立连接或从服务器需要...
在 Redis 的主从复制中,主节点负责接收客户端的写请求,并将修改操作(命令)传播到所有从节点。从节点则定期同步主节点的数据,确保与主节点保持一致。这样,即使主节点出现问题,从节点也能接管服务,保障系统的...
Redis主从复制是其HA的基础,它允许一个Redis实例(主节点)的数据被多个实例(从节点)实时同步。当主节点出现问题时,可以快速切换到一个从节点继续提供服务。以下是主从复制的基本步骤: 1. 配置主节点:在主...
第二句是 `Slaveof 10.0.0.149 6383`,表示从机的 Redis 服务器将复制主机的数据,主机的 IP 地址是 10.0.0.149,端口号是 6383。 配置完成后,需要重启主机和从机的 Redis 服务器,以便生效新的配置。如果配置成功...
Redis主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。 默认情况下,每台Redis服务器都是主节点;且...
为了解决Redis主从Copy的问题,有如下两个解决方案: 主动复制所谓主动复制,就是业务层双写多个Redis,避开Redis自带的主从复制。但是自己干同步,就会产生一致性问题,为了保证主从一致,需要加入一系列的验证机制...