详解Redis复制原理


Posted in Redis onJune 04, 2021

前言

本文主要介绍Redis复制机制

详解Redis复制原理

一.配置与实践

配置

Redis实例分为主节点(master)和从节点(slave),默认情况下都是主节点。每一个从节点只能有一个主节点,但是每一个主节点可以有多个从节点(注意数量,多个从节点会导致主节点写命令多次发送从而过度消耗网络带宽,可用树状结构降低主节点负载)。复制是单向的,只能从主节点复制到从节点。配置复制的方式由以下3种:

  • 在redis-slave.conf配置文件中加入slaveof {masterHost} {masterPort}
  • 在redis-server启动命令后加入 --slaveof {masterHost} {masterPort}
  • 启动后直接使用命令slaveof {masterHost} {masterPort}

综上,Redis支持在启动之前配置,也支持运行中动态配置。

实践

我们用动态配置的方法来配置,先起一个端口为6379的Redis实例,作为主节点:

redis-server /usr/local/Cellar/redis/4.0.9/.bottle/etc/redis.conf

再起一个端口为6380的Redis实例,作为6379的从节点:

redis-server /usr/local/Cellar/redis/4.0.9/.bottle/etc/redis-slave.conf

用客户端连到从节点,使用slaveof命令,slaveof配置都是在从节点发起的。

127.0.0.1:6380> slaveof 127.0.0.1 6379

OK

从节点日志:

75585:S 06 May 16:27:50.389 * Connecting to MASTER 127.0.0.1:6379

75585:S 06 May 16:27:50.389 * MASTER <-> SLAVE sync started

75585:S 06 May 16:27:50.390 * Non blocking connect for SYNC fired the event.

75585:S 06 May 16:27:50.390 * Master replied to PING, replication can continue...

75585:S 06 May 16:27:50.390 * Trying a partial resynchronization (request 47770067272eb8101489fe7c00c8e838125c3aa3:1).

75585:S 06 May 16:27:50.392 * Full resync from master: e91e683b1e13332f97ecb9fa90ecdace460ab4ca:0

75585:S 06 May 16:27:50.392 * Discarding previously cached master state.

75585:S 06 May 16:27:50.491 * MASTER <-> SLAVE sync: receiving 215 bytes from master

75585:S 06 May 16:27:50.492 * MASTER <-> SLAVE sync: Flushing old data

75585:S 06 May 16:27:50.492 * MASTER <-> SLAVE sync: Loading DB in memory

75585:S 06 May 16:27:50.492 * MASTER <-> SLAVE sync: Finished with success

主节点日志:

75553:M 06 May 16:27:50.391 * Slave 127.0.0.1:6380 asks for synchronization

75553:M 06 May 16:27:50.391 * Partial resynchronization not accepted: Replication ID mismatch (Slave asked for '47770067272eb8101489fe7c00c8e838125c3aa3', my replication IDs are '160af1c75f86edc50186e3e4a4dc6ecb5e3fa586' and '0000000000000000000000000000000000000000')

75553:M 06 May 16:27:50.391 * Starting BGSAVE for SYNC with target: disk

75553:M 06 May 16:27:50.391 * Background saving started by pid 75675

75675:C 06 May 16:27:50.395 * DB saved on disk

75553:M 06 May 16:27:50.490 * Background saving terminated with success

75553:M 06 May 16:27:50.491 * Synchronization with slave 127.0.0.1:6380 succeeded

可以看到,第一次建立复制关系的时候,主节点和从节点进行了一次全量复制,见图:

详解Redis复制原理

当完成复制的建立之后,接下来主节点会持续的把写命令发送给从节点,保证主从数据一致。

在主实例上添加新的key:

127.0.0.1:6379> set Lin 112131

OK

在从实例查看刚刚添加的key:

127.0.0.1:6380> get Lin

"112131"

只读

由于复制只能从主节点到从节点,对于从节点的数据修改主节点无法感知,为了避免主从实例之间的数据不一致。从节点默认配置为只读模式:

slave-read-only yes

二.工作原理

我们先讲3个比较关键的参数:master_replid、master_repl_offset和slave_repl_offset。我们分别在master6379和slave6380上执行info replication

127.0.0.1:6379> info replication

# Replication

role:master

connected_slaves:1

slave0:ip=127.0.0.1,port=6380,state=online,offset=1093,lag=1

master_replid:e91e683b1e13332f97ecb9fa90ecdace460ab4ca

master_replid2:0000000000000000000000000000000000000000

master_repl_offset:1093

second_repl_offset:-1

repl_backlog_active:1

repl_backlog_size:1048576

repl_backlog_first_byte_offset:1

repl_backlog_histlen:1093

 

 

127.0.0.1:6380> info replication

# Replication

role:slave

master_host:127.0.0.1

master_port:6379

master_link_status:up

master_last_io_seconds_ago:9

master_sync_in_progress:0

slave_repl_offset:1107

slave_priority:100

slave_read_only:1

connected_slaves:0

master_replid:e91e683b1e13332f97ecb9fa90ecdace460ab4ca

master_replid2:0000000000000000000000000000000000000000

master_repl_offset:1107

second_repl_offset:-1

repl_backlog_active:1

repl_backlog_size:1048576

repl_backlog_first_byte_offset:1

repl_backlog_histlen:1107

master_replid是master启动时生成的随机字符串,用来标识主实例

master_repl_offset是复制流中的一个偏移量,master处理完写入命令后,会把命令的字节长度做累加记录,统计在该字段。该字段也是实现部分复制的关键字段。

slave_repl_offset同样也是一个偏移量,从节点收到主节点发送的命令后,累加自身的偏移量,通过比较主从节点的复制偏移量可以判断主从节点数据是否一致。

当从实例连接到主实例时,从实例会发送master_replid和master_repl_offset(标识与主实例同步的最后一个快照)请求部分复制。如果主实例接收部分复制的话则从最后一个偏移量开始增量进行部分复制,否则将进行全量复制。如图:

详解Redis复制原理

三.数据同步

Redis在2.8之前使用sync命令完成主从数据同步,Redis在2.8及以上使用psync命令完成主从数据同步,同步过程分为:全量复制和部分复制

全量复制

全量复制是Redis最早支持的复制方式,也是主从第一次建立复制的时候必须经历的。它会把主节点全部数据一次性发送给从节点,当数据量较大的时候,会对主从节点和网络造成很大开销。主节点执行bgsave保存RDB文件,然后将这个文件发送给从节点,从节点收到RDB文件后,会先将内存中的所有数据清除,然后再将RDB文件中的数据导入。

主实例在复制过程中是完全异步的,因此不会阻塞主节点的请求。在这一期间内主节点的所有写入命令数据都保存在从客户端缓冲区(slave client buffer)内,在从节点加载完RDB文件后,主节点会将这个缓冲区的内容发送给从节点。

从客户端缓冲区默认大小限制为:

client-output-buffer-limit slave 256mb 64mb 60

意思是如果60秒内缓冲区消耗持续大于64MB或者直接超过256MB时,主节点将直接关闭复制客户端连接,造成全量同步失败。

部分复制

在高版本的Redis实现中,master_replid和offset存储在RDB文件中。当从实例在复制过程中,因网络闪断等原因造成的数据丢失场景,Redis能够从rdb文件中重新加载master_replid和offset,从而使部分重新同步成为可能。因为补发的数据远小于全量数据,所以可以有效的避免全量复制带来的负载和消耗。

之前说过,从节点连接主节点之后,会使用master_replid和master_repl_offset请求主节点,首先判断master_replid是否和自己的master_replid一致,然后检查请求中的master_repl_offset是否能从缓冲区(replication backlog)中获取,如果偏移量在backlog范围内,那么可以进行部分复制。如果在断开连接期间主节点收到的写入命令的数量超过了backlog缓冲区的容量,那么会进行全量复制。默认情况下backlog为1MB。

以上就是详解Redis复制原理的详细内容,更多关于Redis复制原理的资料请关注三水点靠木其它相关文章!

Redis 相关文章推荐
k8s部署redis cluster集群的实现
Jun 24 Redis
Redis Cluster集群动态扩容的实现
Jul 15 Redis
Redis的字符串是如何实现的
Oct 24 Redis
Redis 持久化 RDB 与 AOF的执行过程
Nov 07 Redis
关于SpringBoot 使用 Redis 分布式锁解决并发问题
Nov 17 Redis
linux下安装redis图文详细步骤
Dec 04 Redis
使用Redis实现点赞取消点赞的详细代码
Mar 20 Redis
解决redis批量删除key值的问题
Mar 23 Redis
Redis 限流器
May 15 Redis
利用Redis实现点赞功能的示例代码
Jun 28 Redis
redis protocol通信协议及使用详解
Jul 15 Redis
Redis主从复制操作和配置详情
Sep 23 Redis
Windows下redis下载、redis安装及使用教程
深入理解redis中multi与pipeline
Jun 02 #Redis
SpringBoot 集成Redis 过程
Jun 02 #Redis
详解Redis基本命令与使用场景
Django使用redis配置缓存的方法
Jun 01 #Redis
详解Redis集群搭建的三种方式
May 31 #Redis
浅谈Redis主从复制以及主从复制原理
You might like
BBS(php &amp; mysql)完整版(八)
2006/10/09 PHP
php数组函数序列之sort() 对数组的元素值进行升序排序
2011/11/02 PHP
Yii把CGridView文本框换成下拉框的方法
2014/12/03 PHP
CodeIgniter针对lighttpd服务器URL重写的方法
2015/06/10 PHP
PHP对称加密函数实现数据的加密解密
2016/10/27 PHP
PHP实现生成推广海报的方法详解
2018/03/14 PHP
js下利用控制器载入对应脚本
2010/07/17 Javascript
javascript面向对象编程代码
2011/12/19 Javascript
javascript event在FF和IE的兼容传参心得(绝对好用)
2014/07/10 Javascript
网页运行时提示对象不支持abigimage属性或方法
2014/08/10 Javascript
javascript学习笔记(二)数组和对象部分
2014/09/30 Javascript
JavaScript中使用Object.create()创建对象介绍
2014/12/30 Javascript
jQuery实现网页抖动的菜单抖动效果
2015/08/07 Javascript
在ASP.NET MVC项目中使用RequireJS库的用法示例
2016/02/15 Javascript
Vuejs第十一篇组件之slot内容分发实例详解
2016/09/09 Javascript
javascript特殊文本输入框网页特效
2016/09/13 Javascript
AngularJS实现DOM元素的显示与隐藏功能
2016/11/22 Javascript
基于JQuery的购物车添加删除以及结算功能示例
2017/03/08 Javascript
vue数据传递--我有特殊的实现技巧
2018/03/20 Javascript
JS实现简单日历特效
2020/01/03 Javascript
JS原型prototype和__proto__用法实例分析
2020/03/14 Javascript
[02:16]2018年度CS GO最具人气选手-完美盛典
2018/12/16 DOTA
Python 2.7.x 和 3.x 版本的重要区别小结
2014/11/28 Python
Python用UUID库生成唯一ID的方法示例
2016/12/15 Python
使用python实现kNN分类算法
2019/10/16 Python
一文了解python 3 字符串格式化 F-string 用法
2020/03/04 Python
TensorFlow tf.nn.softmax_cross_entropy_with_logits的用法
2020/04/19 Python
idea2020手动安装python插件的实现方法
2020/07/17 Python
中科方德软件测试面试题
2016/04/21 面试题
MYSQL相比于其他数据库有哪些特点
2013/07/19 面试题
2014年妇委会工作总结
2014/12/10 职场文书
班主任寄语2015
2015/02/26 职场文书
2015年医院护理部工作总结
2015/04/23 职场文书
法定代表人免职证明
2015/06/24 职场文书
《正比例》教学反思
2016/02/23 职场文书
吃通javascript正则表达式
2021/04/21 Javascript