您当前的位置:首页 > 电脑百科 > 数据库 > Redis

Redis如何高效可靠地实现主从复制?终于有人讲明白了

时间:2019-10-28 14:54:29  来源:  作者:
导读:本文内容主要包括redis主从复制功能的概述、作用和方案实施。

作者:李乐

来源:华章科技

Redis如何高效可靠地实现主从复制?终于有人讲明白了

 

Redis支持主从复制功能,用户可以通过执行slaveof命令或者在配置文件中设置slaveof选项来开启复制功能。例如,现在有两台服务器—127.0.0.1:6379和127.0.0.1:7000,向服务器127.0.0.1:6379发送下面命令:

127.0.0.1:6379>slaveof 127.0.0.1 7000
OK

此时服务器127.0.0.1:6379会成为服务器127.0.0.1:7000的从服务器(slaver),服务器127.0.0.1:7000会成为服务器127.0.0.1:6379的主服务器(master);通过复制功能,从服务器127.0.0.1:6379的数据可以和主服务器127.0.0.1:7000的数据保持同步。

为什么需要主从复制功能呢?

简单来说,主从复制功能主要有以下两点作用。

  1. 读写分离,单台服务器能支撑的QPS是有上限的,我们可以部署一台主服务器、多台从服务器,主服务器只处理写请求,从服务器通过复制功能同步主服务器数据,只处理读请求,以此提升Redis服务能力;另外我们还可以通过复制功能来让主服务器免于执行持久化操作:只要关闭主服务器的持久化功能,然后由从服务器去执行持久化操作即可。
  2. 数据容灾,任何服务器都有宕机的可能,我们同样可以通过主从复制功能提升Redis服务的可靠性;由于从服务器与主服务器数据保持同步,一旦主服务器宕机,可以立即将请求切换到从服务器,从而避免Redis服务中断。

对于本例来说slaveof命令的主要流程如下。

  1. 从服务器127.0.0.1:6379向主服务器127.0.0.1:7000发送sync命令,请求同步数据。
  2. 主服务器127.0.0.1:7000接收到sync命令请求,开始执行bgsave命令持久化数据到RDB文件,并且在持久化数据期间会将所有新执行的写入命令都保存到一个缓冲区。
  3. 当持久化数据执行完毕后,主服务器127.0.0.1:7000将该RDB文件发送给从服务器127.0.0.1:6379,从服务器接收该RDB文件,并将文件中的数据加载到内存
  4. 主服务器127.0.0.1:7000将缓冲区中的命令请求发送给从服务器127.0.0.1:6379。
  5. 每当主服务器127.0.0.1:7000接收到写命令请求时,都会将该命令请求按照Redis协议格式发送给从服务器127.0.0.1:6379,从服务器接收并处理主服务器发送过来的命令请求。

上述流程已经可以完成主从复制基本功能了,Redis 2.8以前就是这样实现的,但是注意到步骤2中存在持久化操作(bgsave),而这是一个非常耗费资源的操作。

Redis如何高效可靠地实现主从复制?终于有人讲明白了

 

举一个简单的例子。

主服务器和从服务器之间是通过TCP长连接交互数据的,假设某个时刻主从服务器之间的网络连接发生故障且时间比较短,在此期间主服务器只执行了很少的写命令请求。

待主从服务器之间的网络连接恢复后,从服务器会重新连接到主服务器,并发送sync命令请求同步数据。这时候主服务器还需要执行持久化操作吗?显然是可以避免的,只要主服务器能够缓存连接故障期间执行的写命令即可。

Redis 2.8提出了新的主从复制解决方案。从服务器会记录已经从主服务器接收到的数据量(复制偏移量);而主服务器会维护一个复制缓冲区,记录自己已执行且待发送给从服务器的命令请求,同时还需要记录复制缓冲区第一个字节的复制偏移量。从服务器请求同步主服务器的命令也改为了psync。

当从服务器连接到主服务器时,会向主服务器发送psync命令请求同步数据,同时告诉主服务器自己已经接收到的复制偏移量,主服务器判断该复制偏移量是否还包含在复制缓冲区;如果包含,则不需要执行持久化操作,直接向从服务器发送复制缓冲区中命令请求即可,这称为部分重同步;如果不包含,则需要执行持久化操作,同时将所有新执行的写命令缓存在复制缓冲区中,并重置复制缓冲区第一个字节的复制偏移量,这称为完整重同步。

详情可参照Redis源码,方法masterTryPartialResynchronization用于判断是否可以执行部分重同步;方法replicationFeedSlaves用于向所有从服务器广播命令。

void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc){
 if (server.repl_backlog) {
 //将当前命令请求添加到复制缓冲区
 }
 while((ln = listNext(&li))) {
 //向所有从服务器同步命令请求
 }
}

另外,从服务器也会通过命令“REPLCONF ACK < reploff >”定时向主服务器汇报自己的复制偏移量;据此,主服务器一来可以检测从服务器是否有效,二来可以重新广播丢失的命令请求。

另外需要注意的是,每台Redis服务器都有一个运行ID,从服务器每次发送psync请求同步数据时,会携带自己需要同步主服务器的运行ID。

主服务器接收到psync命令时,需要判断命令参数运行ID与自己的运行ID是否相等,只有相等才有可能执行部分重同步。而当从服务器首次请求主服务器同步数据时,从服务器显然是不知道主服务器的运行ID,此时运行ID以“?”填充,同时复制偏移量初始化为-1。

从上面的分析我们可以得到psync命令格式为“psync <MASTER_RUN_ID> <OFFSET>”,主从复制初始化流程如图1所示。

从图1可以看到,当主服务器判断可以执行部分重同步时向从服务器返回“+CON-TINUE”;需要执行完整重同步时向从服务器返回“+FULLRESYNC RUN_ID OFFSET”,其中RUN_ID为主服务器自己的运行ID,OFFSET为复制偏移量。

Redis如何高效可靠地实现主从复制?终于有人讲明白了

▲图1 主从复制初始化流程图

可以看到执行部分重同步的要求还是比较严格的:

  1. RUN_ID必须相等;
  2. 复制偏移量必须包含在复制缓冲区中。

然而在生产环境中,经常会出现以下两种情况:

  • 从服务器重启(复制信息丢失);
  • 主服务器故障导致主从切换(从多个从服务器重新选举出一台机器作为主服务器,主服务器运行ID发生改变)。

这时显然是无法执行部分重同步的,而这两种情况又很常见,因此Redis 4.0针对主从复制又提出了两点优化,提出了psync2协议。

  • 方案1:持久化主从复制信息

Redis服务器关闭时,将主从复制信息(复制的主服务器RUN_ID与复制偏移量)作为辅助字段存储在RDB文件中;Redis服务器启动加载RDB文件时,恢复主从复制信息,重新同步主服务器时携带。持久化主从复制信息代码如下:

if (rdbSaveAuxFieldStrStr(rdb,"repl-id",server.replid)
 == -1) return -1;
 if (rdbSaveAuxFieldStrInt(rdb,"repl-offset",server.master_repl_offset)
 == -1) return -1;
  • 方案2:存储上一个主服务器复制信息

当主服务器发生故障,自己成为新的主服务器时,使用变量server.replid2和server.second_replid_offset存储之前主服务器的运行ID与复制偏移量:

void shiftReplicationId(void) {
 memcpy(server.replid2,server.replid,sizeof(server.replid));
 server.second_replid_offset = server.master_repl_offset+1;
 changeReplicationId();
}

另外判断是否能执行部分重同步的条件也改变为:

if (strcasecmp(master_replid, server.replid) &&
 (strcasecmp(master_replid, server.replid2) ||
 psync_offset > server.second_replid_offset))
{
 goto need_full_resync;
}

假设m为主服务器(运行ID为M_ID),A、B和C为三个从服务器;某一时刻主服务器m发生故障,从服务器A升级为主服务器(同时会记录replid2=M_ID),从服务器B和C重新向主服务器A发送“psync M_ID psync_offset”请求;显然根据上面条件,只要psync_offset满足条件,就可以执行部分重同步。

关于作者:李乐,好未来php工程师,西安电子科技大学硕士,乐于钻研技术与源码研究,对Redis和Nginx有较深理解。合著书籍《Redis 5设计与源码分析》。

本文摘编自《Redis 5设计与源码分析》,经出版方授权发布。



Tags:Redis 主从复制   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
redis 主从复制 master 节点提供数据,也就是写。slave 节点负责读。不是说master 分支不能读数据,也能只是我们希望将读写进行分离。slave 是不能写数据的,只能处理读请求主从...【详细内容】
2020-06-21  Tags: Redis 主从复制  点击:(77)  评论:(0)  加入收藏
我们知道,当有多台 Redis 服务器时,肯定就有一台主服务器和多台从服务器。一般来说,主服务器进行写操作,从服务器进行读操作。...【详细内容】
2020-02-16  Tags: Redis 主从复制  点击:(100)  评论:(0)  加入收藏
Redis支持主从复制功能,用户可以通过执行slaveof命令或者在配置文件中设置slaveof选项来开启复制功能。例如,现在有两台服务器—127.0.0.1:6379和127.0.0.1:7000,向服务器...【详细内容】
2019-10-28  Tags: Redis 主从复制  点击:(92)  评论:(0)  加入收藏
Redis的主从复制机制是指可以让从服务器(slave)能精确复制主服务器(master)的数据,如下图所示:...【详细内容】
2019-09-06  Tags: Redis 主从复制  点击:(156)  评论:(0)  加入收藏
在这篇文章,我们一起了解 Redis 使用中非常重要的两个机制:Reids 持久化和主从复制。 图片来自 Unsplash什么是 Redis 持久化?Redis 作为一个键值对内存数据库(NoSQL),数据都...【详细内容】
2019-08-01  Tags: Redis 主从复制  点击:(251)  评论:(0)  加入收藏
▌简易百科推荐
来源: my.oschina.net/xiaomu0082/blog/2990388首先说下问题现象:内网sandbox环境API持续1周出现应用卡死,所有api无响应现象刚开始当测试抱怨环境响应慢的时候 ,我们重启一下应...【详细内容】
2021-12-08  Java识堂    Tags:Redis   点击:(18)  评论:(0)  加入收藏
我不知道为什么你会选择对特定数量的“错误”(或警告)如此具体。听起来您正在寻找将要发布到 Yahoo! 的某些文章的内容。 Insider (N Foos to Blah for the BlahBlah)。那说:...【详细内容】
2021-12-07  富集云科技有限公司    Tags:Redis   点击:(14)  评论:(0)  加入收藏
目录 一、背景 二、步骤 0.理论支持 1、获取数据 2、结果 3、分析数据并评估大小 三、关于repl-backlog-size 一、背景 repl-backlog-size控制这个环形缓冲区. ​ 主从断...【详细内容】
2021-11-05  弈秋的美好生活    Tags:redis   点击:(41)  评论:(0)  加入收藏
Redis 性能测试是通过同时执行多个命令实现的。1,Redis-benchmarkRedis性能命令:redis性能命令格式: redis-benchmark [option] [option value] redis 性能测试工具可选参数如...【详细内容】
2021-11-02  川石信息    Tags:Redis   点击:(41)  评论:(0)  加入收藏
1 概述数据结构和内部编码 无传统关系型数据库的 Table 模型schema 所对应的db仅以编号区分。同一 db 内,key 作为顶层模型,它的值是扁平化的。即 db 就是key的命名空间。 key...【详细内容】
2021-11-01  JavaEdge    Tags:Redis   点击:(28)  评论:(0)  加入收藏
普通java中使用引用Java redis 驱动,即可连接:import redis.clients.jedis.Jedis; public class RedisTestJava { public static void main(String[] args) { //连...【详细内容】
2021-10-13  faesuite    Tags:Redis   点击:(34)  评论:(0)  加入收藏
Redis常用的数据结构有 string list set zset hashstringstring 是 Redis 的基本的数据类型,一个 key 对应一个 value。string 类型是二进制安全的,Redis的string可以包含任...【详细内容】
2021-10-12  语霖    Tags:Redis   点击:(36)  评论:(0)  加入收藏
列表类型可以存储一组按插入顺序排序的字符串,它非常灵活,支持在两端插入、弹出数据,可以充当栈和队列的角色。> LPUSH fruit apple(integer) 1> RPUSH fruit banana(integer)...【详细内容】
2021-09-17  深夜敲代码    Tags:Redis   点击:(54)  评论:(0)  加入收藏
Redis持久化意义 是做灾难恢复,数据恢复,也可以归类到高可用的一个环节里面去,比如你的redis整个挂了,然后redis就不可用了,你要做的事情是让redis变得可用,尽快变得可用 大量的请...【详细内容】
2021-08-12  小李说IT    Tags:Redis   点击:(77)  评论:(0)  加入收藏
当查询Redis中没有的数据时,该查询会下沉到数据库层,同时数据库层也没有该数据,当这种情况大量出现或被恶意攻击时,接口的访问全部透过Redis访问数据库,而数据库中也没有这些数据...【详细内容】
2021-07-30  随便t    Tags:缓存穿透   点击:(91)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条