Redis之主从服务器的复制

本文主要介绍Redis集群中主从服务器复制功能的实现。

在Redis中,用户可以通过执行SLAVEOF命令或设置slaveof选项,让一个服务器去复制另一个服务器,称被复制的服务器为主服务器(master),而对主服务器进行复制的服务器称为从服务器(slave)

例如:

127.0.0.1:12345> SLAVEOF 127.0.0.1 6379
OK

则服务器127.0.0.1:12345就成为了127.0.0.1 6379的slave,127.0.0.1 6379成为了127.0.0.1:12345的master。

进行复制中的主从服务器双方的数据库保存同样的数据,概念上称之为数据库的一致性

I、旧版复制功能的实现

Redis的复制功能分为同步(sync)命令传播(command propagate)两个操作:

· 同步操作用于将从服务器的数据库状态更新至主服务器当前所处的数据库状态。
· 命令传播操作则用于主服务器的数据被修改后,让主从服务器的数据库状态重新回到一致。

1.1 同步

从服务器对主服务器的同步操作需要通过向主服务器发送SYNC命令来完成,以下是SYNC命令的执行步骤:
1、 从服务器向主服务器发送SYNC命令。
2、收到命令之后的主服务器执行BDSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令。
3、当主服务器BGSAVE命令执行完毕时,主服务器会将生成的RDB文件发送给从服务器,从服务器load这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时数据库状态。
4、主服务器将记录在缓冲区中的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器当前所处状态。

下图展示了这一过程:

下图说明了一个同步实例

1.2 命令传播

主服务器每次执行写名列会同时将命令发送给从服务器执行,从而实现命令传播,达到主从服务器的一致性。

II、旧版复制功能的缺点

从服务器在除了在初次复制主服务器之外,还会在断线后重复制是也进行赋值操作。而刚刚介绍的旧版复制功能对于在断线后重复制情况来说,效率很低。

这是由于需要重写执行SYNC操作引起的(一旦执行就会发生RDB的复制等操作)。
如下图所示:

而要解决这个问题的关键就在于上图中红圈位置的复制操作,我们应该尽量不再重新使用SYNC命令,而采用其他方式。

III、新版复制功能的实现

在Redis2.8版本之后,采用了PSYNC命令的代替了SYNC同步操作。

PSYNC命令具有完整重同步部分重同步两种模式:

· 完整重同步与旧版本的SYNC命令一样。
· 部分重同步专门用于处理断线后重复制的情况: 当从服务器在断线后重新连接主服务器时,如果条件允许,主服务器可以将主从服务器连接断开期间执行的写命令发送给从服务器,从服务器只要接收并执行这些写命令,就可以将数据块更新至最新状态。

下图展示了相同情况下使用部分重同步的实例:

IV、部分重同步的实现

部分重同步功能由以下三个部分构成:
· 主服务器的复制偏移量和从服务器的复制偏移量
· 主服务器的复制积压缓冲区
· 服务器的运行ID

4.1 复制偏移量

执行复制的双方都要维护一个复制偏移量
· 主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量+N;
· 从服务器每次收到主服务器传播来的N个字节的数据时,就将自己的复制偏移量+N;

复制偏移量具有两个作用:

· 通过对比主从服务器的复制偏移量,程序可以很容易的知道主从服务器的数据是否处于一致状态;
· 通过对比主从服务器的复制偏移量,可以知道从服务器丢失了哪些数据,从而执行部分重同步。

4.2 复制积压缓冲区

复制积压缓冲区是由主服务器维护的一个固定长度的先进先出队列。

当服务器执行命令传播时,不仅会将写命令传播给从服务器,还会将写命令写入复制积压缓冲区中:

当从服务器重新连上主服务器时,从服务器会通过PSYNC命令将自己的复制偏移量offset发送给主服务器,主服务器会根据这个复制偏移量来决定对从服务器执行何种同步操作:
· 如果offset偏移量之后的数据仍然存在于复制积压缓冲区中,则主服务器将对从服务器执行部分重同步
· 如果offset偏移量之后的数据已经不在复制积压缓冲区中,主服务器会对从服务器执行完整的重同步操作;

4.3 服务器运行ID

每个Redis服务器,都会有自己的运行ID,实现部分重同步需要复制偏移量复制积压缓冲区运行ID一起完成。

当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID发送给从服务器,从服务器将主服务器的运行ID保存起来。

当从服务器断线并重写连接上一个主服务器之后(这个主服务器可能已经不是之前的主服务器,可以参见Redis中的sentinel),从服务器向当前连接的主服务器发送自己之前保存的运行ID:
· 如果从服务器保存的运行ID和当前主服务器的运行ID相同,则说明从服务器断线之前复制的就是当前连接的这个主服务器,主服务器可以尝试执行部分重同步操作
· 如果主服务器发现与自己的运行ID不同,则只能执行完整重同步操作;

V、PSYNC命令的实现

PSYNC命令的调用方法有两种:
1、 如果从服务器以前没有复制过任何主服务器,或者之前执行过SLAVEOF no one命令,则从服务器在开始一次新的复制时将向主服务器发送PSYNC ? -1命令,主动请求主服务器进行完整重同步

2、 如果从服务器已经复制过某个主服务器,则从服务器在开始一次新的复制时会向主服务器发送PSYNC <runid> <offset>命令,runid为上一次保存的主服务器运行ID,offset为从服务器维护的复制偏移量。

主服务器根据接收到的PSYNC命令,会执行下面的三种回复:
1、 主服务器返回+FULLRESYNC <runid> <offset>回复,表示主服务器与从服务器执行完整重同步,runid为主服务器自己的运行ID,offset为主服务器自身的复制偏移量,从服务器将用这个值初始化自己的偏移量。
2、 主服务器回复+COUTINUE,表示主服务器将与从服务器执行部分重同步,根据接收到的offset,已经主服务器自己的复制积压缓冲区来完成。
3、 主服务器回复-ERR,表示主服务器版本低于Redis2.8,无法识别PSYNC命令,之后从服务器将发送SYNC,执行低版本的完整重同步。

下图完整描述了这一过程:

VI、复制的实现

6.1 步骤1:设置主服务器的地址和端口

客户端向从服务器发送:

127.0.0.1:12345> SLAVEOF 127.0.0.1 6379
OK

从服务器首先要做的是将客户端给定的主服务器IP地址127.0.0.1以及端口6379保存到服务器状态的masterhost和masterport属性中:

struct redisServer {
    //主服务器地址
    char *masterhost;
    //端口
    int masterport;
};
6.2 步骤2: 建立套接字连接

从服务器根据命令的IP地址和端口,创建向主服务器的套接字连接:

创建套接字成功,则从服务器会专门为这个套接字关联一个用于复制工作的文件事件处理器,负责执行后续的复制工作。

主服务器在accept从服务器的套接字连接之后,将为该套接字创建相应的客户端状态,并将从服务器看作是一个连接到主服务器的客户端来对待,这时从服务器将同时具有服务器和客户端两个身份
因为复制工作接下来的几个步骤都会以从服务器向主服务器发送命令请求的形式进行,所以理解从服务器是主服务器的客户端相当重要。

6.3 步骤3: 发送PING命令

从服务器成为主服务器的客户端之后,向主服务器发送PING命令:

这个PING命令有两个作用:
1、检查套接字的读写状态是否正常;
2、检查主服务器是否能正常处理请求。

而从服务器会根据主服务器的相应回复状态判断下一步的动作:
1、如果主服务器向从服务器回复一个命令,但从服务器在规定时间内无法读取出命令回复的内容,则表示主从服务器之间的网络连接状态不佳。
从服务器会断开并重新创建连向主服务器的套接字;
2、如果主服务器返回了一个错误,表示主服务器暂时无法处理从服务器的命令请求,从服务器同样断开并重新创建连向主服务器的套接字;
3、从服务器读取到了PONG命令,表示主从服务器网络连接正常,继续执行下面的任务。

6.4 步骤4: 身份验证

身份验证步骤可以通过下图来说明:

6.5 步骤5: 发送端口信息

在执行完身份验证步骤之后,从服务器会向主服务器发送从服务器的监听端口号(用于后续的命令传播等操作),主服务器将端口号记录在从服务器所对应的客户端状态中:

typedef struct redisClient {
    //从服务器的监听端口号
    int slave_listening_port;

} redisClient;
6.6 步骤6: 同步

从服务器向主服务器发送PSYNC命令,执行同步操作,并将自己的数据库更新至主服务器数据库当前状态。

在同步操作执行之前,只有从服务器是主服务器的客户端,但在执行同步操作之后,主服务器也会成为从服务器的客户端
· 如果要执行完整重同步操作,主服务器需要成为从服务器的客户端,才能将保存在缓冲区中的写命令发送给从服务器执行;
· 如果执行的部分重同步操作,主服务器需要成为从服务器客户端,才能将复制积压缓冲区的内容发送给从服务器执行。

因此,在同步操作执行之后,主从服务器都是双方的客户端,它们可以互相向对方发送命令请求,或者互相向对方返回命令回复:

6.7 步骤7: 命令传播

主服务器将自己的写命令发送给从服务器执行,进入命令传播阶段,这一操作的基础也是主服务器时从服务器的客户端

VII、心跳检测

在命令传播阶段,从服务器会以每秒一次的频率向主服务器发送心跳检测命令:
REPLCONF ACK <replication_offset>
replication_offset是从服务器当前的复制偏移量。

REPLCONF ACK命令对主从服务器有三个作用:
· 检测网络连接状态;
· 辅助实现min-slaves选项(min-slaves表示最小从服务器数量,可以防止主服务器在不安全的状态执行写命令,不安全的状态指从服务器数量少于某个数值);
· 检测命令丢失(根据复制偏移量的对比);

【参考】
[1] 《Redis设计与实现》

欢迎转载,转载请注明出处wenmingxing Redis之主从服务器的复制

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342