一文搞定 Redis 复制(全会的举个手看看)
本文大纲
复制过程
数据间的同步
全量复制
部分复制
心跳
异步复制
总结
一、复制过程
Step 1:从节点执行 slaveof 命令。
Step 2:从节点只是保存了 slaveof 命令中主节点的信息,并没有立即发起复制。
Step 3:从节点内部的定时任务发现有主节点的信息,开始使用 socket 连接主节点。
Step 4:连接建立成功后,发送 ping 命令,希望得到 pong 命令响应,否则会进行重连。
Step 5:如果主节点设置了权限,那么就需要进行权限验证,如果验证失败,复制终止。
Step 6:权限验证通过后,进行数据同步,这是耗时最长的操作,主节点将把所有的数据全部发送给从节点。
Step 7:当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下来,主节点就会持续的把写命令发送给从节点,保证主从数据一致性。
二、数据间的同步
上面说的复制过程,其中有一个步骤是“同步数据集”,这个就是现在讲的“数据间的同步”。
redis 同步有 2 个命令:sync 和 psync,前者是 redis 2.8 之前的同步命令,后者是 redis 2.8 为了优化 sync 新设计的命令。我们会重点关注 2.8 的 psync 命令。
主从节点各自复制偏移量
主节点复制积压缓冲区
主节点运行 ID
参与复制的主从节点都会维护自身的复制偏移量。
主节点在处理完写入命令后,会把命令的字节长度做累加记录,统计信息在 info replication 中的 masterreploffset 指标中。
从节点每秒钟上报自身的的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移量。
从节点在接收到主节点发送的命令后,也会累加自身的偏移量,统计信息在 info replication 中。
通过对比主从节点的复制偏移量,可以判断主从节点数据是否一致。
复制积压缓冲区是一个保存在主节点的一个固定长度的先进先出的队列,默认大小 1MB。
这个队列在 slave 连接是创建。这时主节点响应写命令时,不但会把命令发送给从节点,也会写入复制缓冲区。
他的作用就是用于部分复制和复制命令丢失的数据补救。通过 info replication 可以看到相关信息。
每个 redis 启动的时候,都会生成一个 40 位的运行 ID。
运行 ID 的主要作用是用来识别 Redis 节点。如果使用 ip+port 的方式,那么如果主节点重启修改了 RDB/AOF 数据,从节点再基于偏移量进行复制将是不安全的。所以,当运行 id 变化后,从节点将进行全量复制。也就是说,redis 重启后,默认从节点会进行全量复制。
可以通过 debug reload 命令重新加载 RDB 并保持运行 ID 不变,从而有效的避免不必要的全量复制。
缺点是:debug reload 命令会阻塞当前 Redis 节点主线程,因此对于大数据量的主节点或者无法容忍阻塞的节点,需要谨慎使用。一般通过故障转移机制可以解决这个问题。
命令格式为 psync{runId}{offset}
runId:从节点所复制主节点的运行 id
offset:当前从节点已复制的数据偏移量
流程说明:
从节点发送 psync 命令给主节点,runId 就是目标主节点的 ID,如果没有默认为 -1,offset 是从节点保存的复制偏移量,如果是第一次复制则为 -1.主节点会根据 runid 和 offset 决定返回结果:
如果回复 +FULLRESYNC {runId} {offset} ,那么从节点将触发全量复制流程。
如果回复 +CONTINUE,从节点将触发部分复制。
如果回复 +ERR,说明主节点不支持 2.8 的 psync 命令,将使用 sync 执行全量复制。
到这里,数据之间的同步就讲的差不多了,篇幅还是比较长的。主要是针对 psync 命令相关之间的介绍。
三、全量复制
全量复制是 Redis 最早支持的复制方式,也是主从第一次建立复制时必须经历的的阶段。触发全量复制的命令是 sync 和 psync。之前说过,这两个命令的分水岭版本是 2.8,redis 2.8 之前使用 sync 只能执行全量不同,2.8 之后同时支持全量同步和部分同步。
流程如下:
Step 1:发送 psync 命令(spync ?-1)
Step 2:主节点根据命令返回 FULLRESYNC
Step 3:从节点记录主节点 ID 和 offset
Step 4:主节点 bgsave 并保存 RDB 到本地
Step 5:主节点发送 RBD 文件到从节点
Step 6:从节点收到 RDB 文件并加载到内存中
Step 7:主节点在从节点接受数据的期间,将新数据保存到“复制客户端缓冲区”,当从节点加载 RDB 完毕,再发送过去。(如果从节点花费时间过长,将导致缓冲区溢出,最后全量同步失败)
Step 8:从节点清空数据后加载 RDB 文件,如果 RDB 文件很大,这一步操作仍然耗时,如果此时客户端访问,将导致数据不一致,可以使用配置slave-server-stale-data 关闭.
Step 9:从节点成功加载完 RBD 后,如果开启了 AOF,会立刻做 bgrewriteaof。
以上加粗的部分是整个全量同步耗时的地方。
注意:
如过 RDB 文件大于 6GB,并且是千兆网卡,Redis 的默认超时机制(60 秒),会导致全量复制失败。可以通过调大 repl-timeout 参数来解决此问题。
Redis 虽然支持无盘复制,即直接通过网络发送给从节点,但功能不是很完善,生产环境慎用。
四、部分复制
当从节点正在复制主节点时,如果出现网络闪断和其他异常,从节点会让主节点补发丢失的命令数据,主节点只需要将复制缓冲区的数据发送到从节点就能够保证数据的一致性,相比较全量复制,成本小很多。
当从节点出现网络中断,超过了 repl-timeout 时间,主节点就会中断复制连接。
主节点会将请求的数据写入到“复制积压缓冲区”,默认 1MB。
当从节点恢复,重新连接上主节点,从节点会将 offset 和主节点 id 发送到主节点。
主节点校验后,如果偏移量的数后的数据在缓冲区中,就发送 cuntinue 响应 —— 表示可以进行部分复制。
主节点将缓冲区的数据发送到从节点,保证主从复制进行正常状态。
五、心跳
主从节点在建立复制后,他们之间维护着长连接并彼此发送心跳命令。
心跳的关键机制如下:
中从都有心跳检测机制,各自模拟成对方的客户端进行通信,通过 client list 命令查看复制相关客户端信息,主节点的连接状态为 flags = M,从节点的连接状态是 flags = S。
主节点默认每隔 10 秒对从节点发送 ping 命令,可修改配置 repl-ping-slave-period 控制发送频率。
从节点在主线程每隔一秒发送 replconf ack{offset} 命令,给主节点上报自身当前的复制偏移量。
主节点收到 replconf 信息后,判断从节点超时时间,如果超过 repl-timeout 60 秒,则判断节点下线。
注意:
为了降低主从延迟,一般把 redis 主从节点部署在相同的机房/同城机房,避免网络延迟带来的网络分区造成的心跳中断等情况。
六、异步复制
主节点不但负责数据读写,还负责把写命令同步给从节点,写命令的发送过程是异步完成,也就是说主节点处理完写命令后立即返回客户度,并不等待从节点复制完成。
异步复制的步骤很简单,如下:
Step 1:主节点接受处理命令。
Step 2:主节点处理完后返回响应结果 。
Step 3:对于修改命令,异步发送给从节点,从节点在主线程中执行复制的命令。
七、总结
本文主要分析了 Redis 的复制原理,包括复制过程,数据之间的同步,全量复制的流程,部分复制的流程,心跳设计,异步复制流程。
其中,可以看出,RDB 数据之间的同步非常耗时。
所以,Redis 在 2.8 版本退出了类似增量复制的 psync 命令,当 Redis 主从直接发生了网络中断,不会进行全量复制,而是将数据放到缓冲区(默认 1MB)里,再通过主从之间各自维护复制 offset 来判断缓存区的数据是否溢出。如果没有溢出,只需要发送缓冲区数据即可,成本很小;反之,则要进行全量复制。因此控制缓冲区大小非常的重要。
作者:五色花的博客
来源:https://www.cnblogs.com/luao/p/10682830.html
·END·
程序员的成长之路
路虽远,行则必至
本文原发于 同名微信公众号「程序员的成长之路」,回复「1024」你懂得,给个赞呗。
回复 [ 520 ] 领取程序员最佳学习方式
回复 [ 256 ] 查看 Java 程序员成长规划
往期精彩回顾
一文搞定 Redis 复制(全会的举个手看看)的更多相关文章
- 一文搞定Redis五大数据类型及应用场景
		
本文学习知识点 redis五大数据类型数据类型:string.hash.list.set.sorted_set 五大类型各自的应用场景 @TOC 1. string类型 1-1 string类型数据的 ...
 - 一文搞定 SonarQube 接入 C#(.NET) 代码质量分析
		
1. 前言 C#语言接入Sonar代码静态扫描相较于Java.Python来说,相对麻烦一些.Sonar检测C#代码时需要预先编译,而且C#代码必须用MSbuid进行编译,如果需要使用SonarQub ...
 - 一文搞懂vim复制粘贴
		
转载自本人独立博客https://liushiming.cn/2020/01/18/copy-and-paste-in-vim/ 概述 复制粘贴是文本编辑最常用的功能,但是在vim中复制粘贴还是有点麻 ...
 - 一文搞定MySQL的事务和隔离级别
		
一.事务简介 事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成. 一个数据库事务通常包含了一个序列的对数据库的读/写操作.它的存在包含有以下两个目的: 为数据库操作序列提供 ...
 - 一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等
		
本文旨在通过爬取一系列博客网站技术文章的实践,介绍一下scrapy这个python语言中强大的整站爬虫框架的使用.各位童鞋可不要用来干坏事哦,这些技术博客平台也是为了让我们大家更方便的交流.学习.提高 ...
 - 搞懂Redis复制原理
		
前言 与大多数db一样,Redis也提供了复制机制,以满足故障恢复和负载均衡等需求.复制也是Redis高可用的基础,哨兵和集群都是建立在复制基础上实现高可用的.复制不仅提高了整个系统的容错能力,还可以 ...
 - 一文搞定Spring Boot + Vue 项目在Linux Mysql环境的部署(强烈建议收藏)
		
本文介绍Spring Boot.Vue .Vue Element编写的项目,在Linux下的部署,系统采用Mysql数据库.按照本文进行项目部署,不迷路. 1. 前言 典型的软件开发,经过" ...
 - Spring Data Redis 详解及实战一文搞定
		
SDR - Spring Data Redis的简称. Spring Data Redis提供了从Spring应用程序轻松配置和访问Redis的功能.它提供了与商店互动的低级别和高级别抽象,使用户免受 ...
 - 21.SpringCloud实战项目-后台题目类型功能(网关、跨域、路由问题一文搞定)
		
SpringCloud实战项目全套学习教程连载中 PassJava 学习教程 简介 PassJava-Learning项目是PassJava(佳必过)项目的学习教程.对架构.业务.技术要点进行讲解. ...
 
随机推荐
- eslint 配合 git  (husky)
			
为了保证每次提交的 git 代码是正确的,为此我们可以使用 eslint 配合 git hook, 在进行git commit 的时候验证eslint规范 如果 eslint 验证不通过,则不能提交. ...
 - WebsiteCrawler
			
看到网上不少py的爬虫功能极强大,可惜对py了解的不多,以前尝试过使用c# WebHttpRequert类来读取网站的html页面源码,然后通过正则表达式筛选出想要的结果,但现在的网站中,多数使用js ...
 - 【转】数据存储——APP 缓存数据线程安全问题探讨
			
http://blog.cnbang.net/tech/3262/ 问题 一般一个 iOS APP 做的事就是:请求数据->保存数据->展示数据,一般用 Sqlite 作为持久存储层,保存 ...
 - Kbuntu16.04添加工作空间
			
工作空间是一个非常方便的功能,可以让开发者每次只聚焦一个屏幕,又能在各个空间中来回切换.有一种屏幕被扩展的感觉. Kbuntu默认只有一个工作空间,需要按如下方式添加: System settings ...
 - 鸟哥的linux私房菜 - 第三章 主机规划与磁盘分区
			
各硬件装置在linux中的文件名 在linux系统中,每个装置都被当成一个档案来对待. 常见的装置与其在linux中的档名: 磁盘分区 磁盘链接的方式与装置文件名的关系 个人计算机常见的磁盘接口有两种 ...
 - Elasticsearch mapping文档相似性算法
			
Elasticsearch allows you to configure a scoring algorithm or similarity per field. The similarityset ...
 - NO2:设置RedHat Linux下的samba开机启动
			
安装的samba默认不是开机启动的,这样每次都要进入系统人为启动,很不方便,当然系统肯定可以设置开机启动的. 因为我的是RedHat Linux系统,支持chkconfig命令直接配置,会简单些,其它 ...
 - laravel 5.4 运行 make:auth 报错
			
Laravel 5.4 migrate时报错: Specified key was too long error 问题根源 MySQL支持的utf8编码最大字符长度为3字节,如果遇到4字节的宽字符就会 ...
 - poj1065 Wooden Sticks[LIS or 贪心]
			
地址戳这.N根木棍待处理,每根有个长x宽y,处理第一根花费1代价,之后当处理到的后一根比前一根长或者宽要大时都要重新花费1代价,否则不花费.求最小花费代价.多组数据,N<=5000 本来是奔着贪 ...
 - Python创建删除文件
			
Python代码如下: import os directory = "E:\\学习日志\\" os.chdir(directory) # 改变当前工作目录 cwd = os.get ...