REdis主从复制之repl_backlog
目录
目录 1
1. 前言 1
2. 配置项 1
3. redisServer 2
4. feedReplicationBacklog-写repl_backlog 3
5. addReplyReplicationBacklog-读repl_backlog 4
1. 前言
注意,repl_backlog只针对部分复制(Partial Replication),而非全量复制。
本文内容基于redis-5.0.5(截至2019/6/6的最新版本),本文深入介绍REdis主从复制的部分复制核心要素repl_backlog,与其相关的配置直接影响主从间的稳定性,对提升集群的稳定性十分重要。
注意REdis的主节点把所有从节点也当作一个Client看待,正常的数据同步并不涉及repl_backlog。当从节点断开重连,这个时候repl_backlog的作用就体现出来了。截至到5.0.5版本,从节点重启用不上repl_backlog,原因是从节点没有保存repl_backlog的信息,无法实现部分同步,但可少量改动REdis源代码,实现从节点重启后的部分复制。
正常情况下,主节点会往从节点连接缓冲区写一份数据,同时往repl_backlog也写一份数据,所有从节点共享同一份repl_backlog,因此可以考虑repl_backlog配置大一点,以容忍从节点更长时间失联。
从节点向主节点发送命令PSYNC,触发部分复制。有关REdis主从复制的细节,请参见《REdis复制研究》。
2. 配置项
REdis的复制分全量复制和部分复制,全量复制是个很重的过程,而部分复制则是轻量的,部分复制实际是一个增量复制。
REdis的主节点创建和维护一个环形缓冲复制队列(即repl_backlog),从节点部分复制(增量复制)的数据均来自于repl_backlog。
主节点只有一个repl_backlog,所有从节点共享,直接相关的配置项有两个:
配置项名 |
配置项说明 |
repl-backlog-size |
环形缓冲复制队列大小,可不带单位,但同时支持单位:b、k、kb、m、mb、g、gb,单位不区分大小写,其中k、m、g间的计算倍数是1000,而kb、mb和gb的计算倍数是1024。 |
repl-backlog-ttl |
环形缓冲复制队列存活时长(所有slaves不可用时,保留repl_backlog多长时间,单位:秒) |
3. redisServer
结构体redisServer是REdis的第一核心结构,repl_backlog是它的组成成员。
struct redisServer { /* My current replication offset */ long long master_repl_offset; /* Accept offsets up to this for replid2. */ long long second_replid_offset; /* Replication backlog for partial syncs */ char *repl_backlog; // 环形缓冲复制队列 /* Backlog circular buffer size */ long long repl_backlog_size; // 环形缓冲复制队列容量 /* Backlog actual data length */ long long repl_backlog_histlen; // 环形缓冲复制队列已用大小(影响是否能部分复制) /* Backlog circular buffer current offset, that is the next byte will'll write to.*/ // 实际上谈不上空闲,因为总是环绕覆盖写, // 理解为最新数据的截止位置更为合适,更新的数据总是从这里开始写入到repl_backlog中。 long long repl_backlog_idx; // 环形缓冲复制队列空闲起始位置(写从这里开始) /* Replication "master offset" of first byte in the replication backlog buffer.*/ long long repl_backlog_off; // 数据在环形缓冲复制队列的起始位置(读从这里开始) /* Time without slaves after the backlog gets released. */ time_t repl_backlog_time_limit; // 环形缓冲复制队列生存时长 /* We have no slaves since that time. Only valid if server.slaves len is 0. */ time_t repl_no_slaves_since; // 无可用从节点的发生时间 /* Min number of slaves to write. */ int repl_min_slaves_to_write; // 最小需写的从节点数 /* Max lag of <count> slaves to write. */ int repl_min_slaves_max_lag; /* Number of slaves with lag <= max_lag. */ int repl_good_slaves_count; /* Send RDB to slaves sockets directly. */ int repl_diskless_sync; // 不落磁盘(无盘)往从节点发送RDB(全量复制) /* Delay to start a diskless repl BGSAVE. */ // 无盘复制时,延迟指定的时长,以等待更多的从节点 int repl_diskless_sync_delay; }; |
4. feedReplicationBacklog-写repl_backlog
/* Add data to the replication backlog. * This function also increments the global replication offset stored at * server.master_repl_offset, because there is no case where we want to feed * the backlog without incrementing the offset. */ // 主要被replicationFeedSlaves调用 // 写len长的数据ptr到repl_backlog中 // repl_backlog是一个环形buffer,不存在溢出的问题,策略是最新数据覆盖最老数据。 // 如果参数len大于repl_backlog_size, // 则repl_backlog没有实际意义, // 因为无法存储一份完整数据。 void feedReplicationBacklog(void *ptr, size_t len) { unsigned char *p = ptr; server.master_repl_offset += len; /* This is a circular buffer, so write as much data we can at every * iteration and rewind the "idx" index if we reach the limit. */ // 因为repl_backlog是环形buffer, // 剩余的空间可能容纳不了len长的数据, // 当不够时,就需要环绕从头开始写, // 因此这里需while循环。 while(len) { // repl_backlog_size为repl_backlog的容量大小, // 由配置项决定repl_backlog_size值决定, // repl_backlog_idx是repl_backlog空闲区域的起始位置, // 这两个值相减得到repl_backlog可用大小。 size_t thislen = server.repl_backlog_size - server.repl_backlog_idx; // 如果thislen大于len,则表示足够容纳 if (thislen > len) thislen = len; memcpy(server.repl_backlog+server.repl_backlog_idx,p,thislen); // 空闲位置往后挪动 server.repl_backlog_idx += thislen; // 如果空闲位置达到容量大小,则环绕回去从0开始 if (server.repl_backlog_idx == server.repl_backlog_size) server.repl_backlog_idx = 0; len -= thislen; p += thislen; // repl_backlog_histlen记录了repl_backlog中的数据大小 server.repl_backlog_histlen += thislen; } // 修正存储在repl_backlog中的数据大小, // 它不可能超过repl_backlog_size值。 if (server.repl_backlog_histlen > server.repl_backlog_size) server.repl_backlog_histlen = server.repl_backlog_size; /* Set the offset of the first byte we have in the backlog. */ server.repl_backlog_off = server.master_repl_offset - server.repl_backlog_histlen + 1; } |
5. addReplyReplicationBacklog-读repl_backlog
当主节点判断可部分复制时,会记录如下日志:
Partial resynchronization request from %s accepted. Sending %lld bytes of backlog starting from offset %lld. |
给从节点的响应头为“+CONTINUE replid\r\n”或“+CONTINUE\r\n”。函数addReplyReplicationBacklog的实现:
/* Feed the slave 'c' with the replication backlog starting from the * specified 'offset' up to the end of the backlog. */ // 被masterTryPartialResynchronization调用 // 而masterTryPartialResynchronization被syncCommand调用(对应命令PSYNC)。 // 从repl_backlog取数据发给slave, // 数据的开始位置由offset指定。 long long addReplyReplicationBacklog(client *c, long long offset) { long long j, skip, len; serverLog(LL_DEBUG, "[PSYNC] Replica request offset: %lld", offset); // repl_backlog_histlen为0, // 表示repl_backlog中无数据。 if (server.repl_backlog_histlen == 0) { serverLog(LL_DEBUG, "[PSYNC] Backlog history len is zero"); return 0; } serverLog(LL_DEBUG, "[PSYNC] Backlog size: %lld", server.repl_backlog_size); serverLog(LL_DEBUG, "[PSYNC] First byte: %lld", server.repl_backlog_off); serverLog(LL_DEBUG, "[PSYNC] History len: %lld", server.repl_backlog_histlen); serverLog(LL_DEBUG, "[PSYNC] Current index: %lld", server.repl_backlog_idx); /* Compute the amount of bytes we need to discard. */ skip = offset - server.repl_backlog_off; serverLog(LL_DEBUG, "[PSYNC] Skipping: %lld", skip); /* Point j to the oldest byte, that is actually our * server.repl_backlog_off byte. */ j = (server.repl_backlog_idx + (server.repl_backlog_size-server.repl_backlog_histlen)) % server.repl_backlog_size; serverLog(LL_DEBUG, "[PSYNC] Index of first byte: %lld", j); /* Discard the amount of data to seek to the specified 'offset'. */ j = (j + skip) % server.repl_backlog_size; /* Feed slave with data. Since it is a circular buffer we have to * split the reply in two parts if we are cross-boundary. */ len = server.repl_backlog_histlen - skip; serverLog(LL_DEBUG, "[PSYNC] Reply total length: %lld", len); while(len) { long long thislen = ((server.repl_backlog_size - j) < len) ? (server.repl_backlog_size - j) : len; serverLog(LL_DEBUG, "[PSYNC] addReply() length: %lld", thislen); addReplySds(c,sdsnewlen(server.repl_backlog + j, thislen)); len -= thislen; j = 0; } return server.repl_backlog_histlen - skip; } |
REdis主从复制之repl_backlog的更多相关文章
- 深入剖析 redis 主从复制
主从概述 redis 支持 master-slave(主从)模式,redis server 可以设置为另一个 redis server 的主机(从机),从机定期从主机拿数据.特殊的,一个 从机同样可以 ...
- [转载] 深入剖析 redis 主从复制
转载自http://www.cnblogs.com/daoluanxiaozi/p/3724299.html 主从概述 redis 支持 master-slave(主从)模式,redis server ...
- (转)深入剖析Redis主从复制
一.主从概述 Redis 支持 Master-Slave(主从)模式,Redis Server 可以设置为另一个 Redis Server 的主机(从机),从机定期从主机拿数据.特殊的,一个从机同样可 ...
- 深入剖析Redis主从复制
[http://sofar.blog.51cto.com/353572/1413024/] [Redis 主从复制的内部协议和机制] 一.主从概述 Redis 支持 Master-Slave( ...
- [原]Redis主从复制各种环境下测试
Redis 主从复制各种环境下测试 测试环境: Linux ubuntu 3.11.0-12-generic 2GB Mem 1 core of Intel(R) Core(TM) i5-3470 C ...
- NoSQL初探之人人都爱Redis:(4)Redis主从复制架构初步探索
一.主从复制架构简介 通过前面几篇的介绍中,我们都是在单机上使用Redis进行相关的实践操作,从本篇起,我们将初步探索一下Redis的集群,而集群中最经典的架构便是主从复制架构.那么,我们首先来了解一 ...
- 【转】 NoSQL初探之人人都爱Redis:(4)Redis主从复制架构初步探索
一.主从复制架构简介 通过前面几篇的介绍中,我们都是在单机上使用Redis进行相关的实践操作,从本篇起,我们将初步探索一下Redis的集群,而集群中最经典的架构便是主从复制架构.那么,我们首先来了解一 ...
- redis+Keepalived实现Redis主从复制
redis+Keepalived实现Redis主从复制: 环境:CentOs6.5Master: 10.10.10.203Slave: 10.10.10.204Virtural IP Addres ...
- 谈谈redis主从复制的重点
Redis主从复制的配置十分简单,它可以使从服务器是主服务器的完全拷贝.下面是关于Redis主从复制的几点重要内容: Redis使用异步复制.但从Redis 2.8开始,从服务器会周期性的应答从复制流 ...
随机推荐
- pytest_04_测试用例setup和teardown
学过unittest的都知道里面用前置和后置setup和teardown非常好用,在每次用例开始前和结束后都去执行一次. 当然还有更高级一点的setupClass和teardownClass,需配合@ ...
- NETRemoting学习笔记
1..NET Remoting概念 1.一种分布式处理方式.从字面意义上看出,他是基于.net平台的一种远程对象开发技术,该技术是将远程计算机中的数据视为分布式对象来进行开发. 2.一种网络通信技术. ...
- C# vb .net实现透视图效果滤镜
在.net中,如何简单快捷地实现Photoshop滤镜组中的透视图效果呢?答案是调用SharpImage!专业图像特效滤镜和合成类库.下面开始演示关键代码,您也可以在文末下载全部源码: 设置授权 第一 ...
- WebServices 使用Session
using System;using System.Collections.Generic;using System.Data;using System.Linq;using System.Web;u ...
- WebRTC 入门教程(二)| WebRTC信令控制与STUN/TURN服务器搭建
WebRTC 入门教程(二)| WebRTC信令控制与STUN/TURN服务器搭建 四月 4, 2019 作者:李超,音视频技术专家.本文首发于 RTC 开发者社区,欢迎在社区留言与作者交流. htt ...
- python 函数的链式调用(一个函数调用使用两个括号)
# python 函数的链式调用 def funcA(a): def funcB(b): for a_each in a: x = funcB(a_each) return x return func ...
- IDEA 导入Module,多个Module在同一个Project 下显示
之前导入过,隔断时间就忘记了,干脆记下来,免得后续找的麻烦 此文章转载自: https://blog.csdn.net/yyym520/article/details/77527976 1.打开IDE ...
- 如何封装一个自己的win7系统并安装到电脑做成双系统
说明: 目前我是刚试玩所以总结得没有很详细,先粗略放一个,下次有时间再分开整理系统封装或者如何制作双系统. 教程参考地址: 1. https://www.sysceo.com/forum/thread ...
- Linux--基本目录
特点 系统版本:CentOS 6.5 Linux目录:一切从根开始 Linux下面的设备(磁盘)不挂载的话没有办法使用,使用设备必须有一个入口,挂载点实质就是一个目录. 目录结构及含义 目录 说明 b ...
- xadmin集成DjangoUeditor,以及编辑器的视频路径配置
稍微讲一下DjangoUeditor的配置,因为之前去找配置的时候东拼西凑的,所以自己写一下自己一步步配置的过程.首先我是再github上去下载下来,因为是当作第三方插件集成到xadmin中,所以不用 ...