全是干货的技术号:

本文已收录在github,欢迎 star/fork:

在Redis复制的基础上(不包括Redis Cluster或Redis Sentinel作为附加层提供的高可用功能),使用和配置主从复制非常简单,能使得从 Redis 服务器(下文称 slave)能精确得复制主 Redis 服务器(下文称 master)的内容。每次当 slave 和 master 之间的连接断开时, slave 会自动重连到 master 上,并且无论这期间 master 发生了什么, slave 都将尝试让自身成为 master 的精确副本。

该系统的运行依靠三个重要机制:

当一个 master 实例和一个 slave 实例连接正常时, master 会发送一连串命令流保持对 slave 的更新,以便将自身数据集的改变复制给 slave,这包括客户端的写入、key 的过期或被逐出等等

当 master 和 slave 断连后,因为网络问题、或者是主从意识到连接超时, slave 重新连接上 master 并会尝试进行部分重同步:这意味着它会尝试只获取在断开连接期间内丢失的命令流

当无法进行部分重新同步时, slave 会请求全量重同步。这涉及到一个更复杂过程,比如master 需要创建所有数据的快照,将之发送给 slave ,之后在数据集更改时持续发送命令流到 slave

Redis使用默认的异步复制,低延迟且高性能,适用于大多数 Redis 场景。但是,slave会异步确认其从master周期接收到的数据量。

客户端可使用 WAIT 命令来请求同步复制某些特定的数据。但是,WAIT 命令只能确保在其他 Redis 实例中有指定数量的已确认的副本:在故障转移期间,由于不同原因的故障转移或是由于 Redis 持久性的实际配置,故障转移期间确认的写入操作可能仍然会丢失。

Redis 复制特点

Redis 使用异步复制,slave 和 master 之间异步地确认处理的数据量

一个 master 可以拥有多个 slave

slave 可以接受其他 slave 的连接。除了多个 slave 可以连接到同一 master , slave 之间也可以像层级连接其它 slave。Redis 4.0 起,所有的 sub-slave 将会从 master 收到完全一样的复制流

Redis 复制在 master 侧是非阻塞的,即master 在一或多 slave 进行初次同步或者是部分重同步时,可以继续处理查询请求

复制在 slave 侧大部分也是非阻塞的。当 slave 进行初次同步时,它可以使用旧数据集处理查询请求,假设在 redis.conf 中配置了让 Redis 这样做的话。否则,你可以配置如果复制流断开, Redis slave 会返回一个 error 给客户端。但是,在初次同步之后,旧数据集必须被删除,同时加载新的数据集。 slave 在这个短暂的时间窗口内(如果数据集很大,会持续较长时间),会阻塞到来的连接请求。自 Redis 4.0 开始,可以配置 Redis 使删除旧数据集的操作在另一个不同的线程中进行,但是,加载新数据集的操作依然需要在主线程中进行并且会阻塞 slave

复制可被用在可伸缩性,以便只读查询可以有多个 slave 进行(例如 O(N) 复杂度的慢操作可以被下放到 slave ),或者仅用于数据安全和高可用

可使用复制来避免 master 将全部数据集写入磁盘造成的开销:一种典型的技术是配置你的 master 的 redis.conf以避免对磁盘进行持久化,然后连接一个 slave ,配置为不定期保存或是启用 AOF。但是,这个设置必须小心处理,因为重启的 master 将从一个空数据集开始:如果一个 slave 试图与它同步,那么这个 slave 也会被清空!

1 单机“危机”

容量瓶颈

机器故障

QPS瓶颈

一主多从

主从复制作用

数据副本

扩展读性能

总结

一个master可以有多个slave

一个slave只能有一个master

数据流向是单向的,master => slave

2 实现复制的操作

如下两种实现方式:

slaveof 命令

异步执行,很耗时间

无需重启,但是不便于配置的管理。

配置

slaveof ip port

slave-read-only yes

虽然可统一配置,但是需要重启。

3 全量复制

master执行bgsave,在本地生成一份RDB快照client-output-buffer-limit slave 256MB 64MB 60

master node将RDB快照发送给salve node,若RDB复制时间超过60秒(repl-timeout),那么slave node就会认为复制失败,可适当调大该参数(对于千兆网卡的机器,一般每秒传输100MB,6G文件,很可能超过60s)

master node在生成RDB时,会将所有新的写命令缓存在内存中,在salve node保存了rdb之后,再将新的写命令复制给salve node

若在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,那么停止复制,复制失败

slave node接收到RDB之后,清空自己的旧数据,然后重新加载RDB到自己的内存中,同时基于旧的数据版本对外提供服务

如果slave node开启了AOF,那么会立即执行BGREWRITEAOF,重写AOF

RDB生成、RDB通过网络拷贝、slave旧数据的清理、slave aof rewrite,很耗费时间

如果复制的数据量在4G~6G之间,那么很可能全量复制时间消耗到1分半到2分钟

3.1 全量复制开销

bgsave时间

RDB文件网络传输时间

从节点清空数据时间

从节点加载RDB的时间

可能的AOF重写时间

3.2 全量同步细节

master 开启一个后台save进程,以便生成一个 RDB 文件。同时它开始缓冲所有从客户端接收到的新的写入命令。当后台save完成RDB文件时, master 将该RDB数据集文件发给 slave, slave会先将其写入磁盘,然后再从磁盘加载到内存。再然后 master 会发送所有缓存的写命令发给 slave。这个过程以指令流的形式完成并且和 Redis 协议本身的格式相同。

当主从之间的连接因为一些原因崩溃之后, slave 能够自动重连。如果 master 收到了多个 slave 要求同步的请求,它会执行一个单独的后台保存,以便于为多个 slave 服务。

4 增量复制

如果全量复制过程中,master-slave网络连接中断,那么salve重连master时,会触发增量复制

master直接从自己的backlog中获取部分丢失的数据,发送给slave node

msater就是根据slave发送的psync中的offset来从backlog中获取数据的

5 master关闭持久化时的复制安全性

在使用 Redis 复制功能时的设置中,推荐在 master 和 slave 中启用持久化。

当不可能启用时,例如由于非常慢的磁盘性能而导致的延迟问题,应该配置实例来避免重启后自动重新开始复制。

关闭持久化并配置了自动重启的 master 是危险的:

设置节点 A 为 master 并关闭它的持久化设置,节点 B 和 C 从 节点 A 复制数据

节点 A 宕机,但它有一些自动重启系统可重启进程。但由于持久化被关闭了,节点重启后其数据集是空的!

这时B、C 会从A复制数据,但A数据集空,因此复制结果是它们会销毁自身之前的数据副本!

当 Redis Sentinel 被用于高可用并且 master 关闭持久化,这时如果允许自动重启进程也是很危险的。例如, master 可以重启的足够快以致于 Sentinel 没有探测到故障,因此上述的故障模式也会发生。

任何时候数据安全性都是很重要的,所以如果 master 使用复制功能的同时未配置持久化,那么自动重启进程这项就该被禁用。

6 复制工作原理

每个 master 都有一个 replication ID :一个较大的伪随机字符串,标记了一个给定的数据集。

每个 master 也持有一个偏移量,master 将自己产生的复制流发送给 slave 时,发送多少个字节的数据,自身的偏移量就会增加多少,目的是当有新的操作修改自己的数据集时,它可据此更新 slave 的状态。

复制偏移量即使在没有一个 slave 连接到 master 时,也会自增,所以基本上每一对给定的

Replication ID, offset

都会标识一个 master 数据集的确切版本。

psync

slave使用psync从master复制,psync runid offset

master会根据自身情况返回响应信息:

可能是FULLRESYNC runid offset触发全量复制

可能是CONTINUE触发增量复制

slave 连接到 master 时,它们使用 PSYNC 命令来发送它们记录的旧的 master replication ID 和它们至今为止处理的偏移量。通过这种方式, master 能够仅发送 slave 所需的增量部分。

但若 master 的缓冲区中没有足够的命令积压缓冲记录,或者如果 slave 引用了不再知道的历史记录(replication ID),则会转而进行一个全量重同步:在这种情况下, slave 会得到一个完整的数据集副本,从头开始。即:

若slave重连master,那么master仅会复制给slave缺少的部分数据

若第一次连接master,那么会触发全量复制

7 复制的完整流程

slave如果跟master有网络故障,断开连接会自动重连。

master如果发现有多个slave都重新连接,仅会启动一个rdb save操作,用一份数据服务所有slave。

slave启动,仅保存master的信息,包括master的host和ip,但复制流程尚未开始master host和ip配置在 redis.conf 中的 slaveof

slave内部有个定时任务,每s检查是否有新的master要连接和复制,若发现,就跟master建立socket网络连接。

slave发送ping命令给master

口令认证 - 若master设置了requirepass,那么salve必须同时发送masterauth的口令认证

master 第一次执行全量复制,将所有数据发给slave

master后续持续将写命令,异步复制给slave

heartbeat

主从节点互相都会发送heartbeat信息。

master默认每隔10秒发送一次heartbeat,salve node每隔1秒发送一个heartbeat。

8 断点续传

Redis 2.8开始支持主从复制的断点续传

主从复制过程,若网络连接中断,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份。

master和slave都会维护一个offset

master在自身基础上累加offset,slave亦是

slave每秒都会上报自己的offset给master,同时master保存每个slave的offset

master和slave都要知道各自数据的offset,才能知晓互相之间的数据不一致情况。

backlog

master会在内存中维护一个backlog,默认1MB。master给slave复制数据时,也会将数据在backlog中同步写一份。

backlog主要是用做全量复制中断时候的增量复制。

master和slave都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。若master和slave网络连接中断,slave会让master从上次replica offset开始继续复制。但若没有找到对应offset,就会执行resynchronization。

master run id

info server,可见master run id

根据host+ip定位master node,是不靠谱的,如果master node重启或者数据出现了变化,那么slave node应该根据不同的run id区分,run id不同就做全量复制。

如果需要不更改run id重启redis,可使用:

redis-cli debug reload

9 无磁盘化复制

master在内存中直接创建RDB,然后发送给slave,不会在自己本地持久化。

只需要在配置文件中开启repl-diskless-sync yes即可.

等待 5s 再开始复制,因为要等更多 slave 重连

repl-diskless-sync-delay 5

10 处理过期key

Redis 的过期机制可以限制 key 的生存时间。此功能取决于 Redis 实例计算时间的能力,但是,即使使用 Lua 脚本更改了这些 key,Redis slaves 也能正确地复制具有过期时间的 key。

为实现这功能,Redis 不能依靠主从使用同步时钟,因为这是一个无法解决的问题并且会导致 race condition 和数据不一致,所以 Redis 使用三种主要的技术使过期的 key 的复制能够正确工作:

slave 不会让 key 过期,而是等待 master 让 key 过期。当一个 master 让一个 key 到期(或由于 LRU 算法删除)时,它会合成一个 DEL 命令并传输到所有 slave

但由于这是 master 驱动的 key 过期行为,master 无法及时提供 DEL 命令,所以有时 slave 的内存中仍然可能存在逻辑上已过期的 key 。为了处理这问题,slave 使用它的逻辑时钟以报告只有在不违反数据集的一致性的读取操作(从主机的新命令到达)中才存在 key。用这种方法,slave 避免报告逻辑过期的 key 仍然存在。在实际应用中,使用 slave 程序进行缩放的 HTML 碎片缓存,将避免返回已经比期望的时间更早的数据项

在Lua脚本执行期间,不执行任何 key 过期操作。当一个Lua脚本运行时,从概念上讲,master 中的时间是被冻结的,这样脚本运行的时候,一个给定的键要么存在要么不存在。这可以防止 key 在脚本中间过期,保证将相同的脚本发送到 slave ,从而在二者的数据集中产生相同的效果。

一旦 slave 被提升 master ,它将开始独立过期 key,而不需要任何旧 master 帮助。

11 重新启动和故障转移后的部分重同步

Redis 4.0 开始,当一个实例在故障转移后被提升为 master 时,它仍然能够与旧 master 的 slave 进行部分重同步。为此,slave 会记住旧 master 的旧 replication ID 和复制偏移量,因此即使询问旧的 replication ID,也可以将部分复制缓冲提供给连接的 slave 。

但是,升级的 slave 的新 replication ID 将不同,因为它构成了数据集的不同历史记录。例如,master 可以返回可用,并且可以在一段时间内继续接受写入命令,因此在被提升的 slave 中使用相同的 replication ID 将违反一对复制标识和偏移对只能标识单一数据集的规则。

另外,slave 在关机并重新启动后,能够在 RDB 文件中存储所需信息,以便与 master 进行重同步。这在升级的情况下很有用。当需要时,最好使用 SHUTDOWN 命令来执行 slave 的保存和退出操作。

参考

java redis 重连_突破Java面试(23-4) - Redis 复制原理相关推荐

  1. java 通过redis实现倒计时_突破Java面试(42) - Redis amp; ZooKeeper两种分布式锁实现的优劣...

    0 Github 1 面试题 一般实现分布式锁都有哪些方式?使用redis如何设计分布式锁?使用zk来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高? 2 考点分析 一般先问问你zk,然后 ...

  2. redis 哨兵 异步_突破Java面试(23-8) - Redis哨兵主备切换的数据丢失问题-阿里云开发者社区...

    1 数据丢失的两个场景 主备切换的过程,可能会导致数据丢失 1.1 异步复制 由于 master => slave的复制是异步的,所以可能有部分数据还没复制到slave,master就宕机,于是 ...

  3. java 变量重名_关于java中变量的重名问题

    在java语言中,变量的命名有很多规则和规范,但是有的地方可以使用相同的变量名,有的地方却不能使用,这是为什么呢? 下面是个人的一点见解,是从作用域和内存来分析的,目的是为了方便理解 大家可以看下面这 ...

  4. java项目----教务管理系统_基于Java的教务管理系统

    java项目----教务管理系统_基于Java的教务管理系统 2022-04-22 18:18·java基础 最近为客户开发了一套学校用教务管理系统,主要实现学生.课程.老师.选课等相关的信息化管理功 ...

  5. Java基础到就业!项目加面试!之Redis面试大全!倾心整理【建议收藏】

    前前后后,小孟已经帮助很多的小伙伴挺近了大厂. 1,专科的粉丝拿到了阿里的offer,小孟直呼不可思议 2,❤️专科出身拿到阿里offer,小孟直呼666![付硬核面试]❤️ 3,大三下学期挂科,大四 ...

  6. java redis使用卡死_记一次找因 redis 使用不当导致应用卡死 bug 的过程

    原标题:记一次找因 redis 使用不当导致应用卡死 bug 的过程 作者:小木 my.oschina.net/xiaomu0082/blog/2990388 首先说下问题现象:内网sandbox环境 ...

  7. JAVA redis缓存过期_失效java服务器

    Java 并发/多线程教程--4并发模型 本系列译自jakob jenkov的Java并发多线程教程(本章节部分内容参考http://ifeve.com/并发编程模型),个人觉得很有收获.由于个人水平 ...

  8. java 指令重拍_我发现我的Java重拍了!

    java 指令重拍 在一月份,我写了一篇文章,介绍了一些我希望在Java语言中看到的变化,这些变化会让我更加喜欢它(并使它变得更现代). 很多人建议使用许多JVM语言,但我很大程度上不予理会,因为这不 ...

  9. 美国道富java开发面试题_从事Java开发五年,面试9家拿到7家offer,1096面试+67笔试题...

    个人情况 时间总是在不经意间流逝,我们也在人生的旅途上不断前行,转眼间在微软的美国总部工作近两年了.生活总给我们带来新的挑战,同时也有新的惊喜.这两年在陌生的国度里用着不太流利的英语和各种肤色的人交流 ...

最新文章

  1. 教你用深度学习LSTM网络预测流行音乐趋势(附代码)
  2. html5图片加载不了,webView加载html图片遇到的问题解决
  3. 北邮国院c语言期末考试题,北邮C语言复习题2014.ppt
  4. html实现全选 反选,jquery实现全选、不选、反选的两种方法
  5. 如何理解拜占庭将军问题?
  6. SAP License:值得一看的ERP问题
  7. K_Nearest_Neighbot(knn)方法及其Pyhon 实现
  8. Python + Selenium WebDriver Api 知识回顾
  9. 在VS2010进行MFC编程设置字符集的问题(使用多字节字符集引起错误)
  10. 1月8日 官方ros.org太慢,配置中科院ros镜像源 关于解决Ubuntu 14.04安装ros提示校验错误解决
  11. 如何在PPT中插入HTML页面|如何使用控件将Pyecharts图表插入PPT|ActiveX
  12. 这年代,你需要了解一下什么是聚合搜索(附站点推荐)
  13. CCL2022 中文语法纠错评测
  14. Pocket PC 2003 html 的问题
  15. android 时区表以及设置系统时区
  16. 学习python第八节课:可变与不可变对象
  17. Web端和App端测试区别
  18. 将m3u8文件转MP4、TS、flv、mkv等文件
  19. HL7 标准及实现指南 必看的网址
  20. PNAS:问微生物群落演替之随机性与确定性过程-谁主沉浮

热门文章

  1. Mysql索引示例_MYSQL索引实例
  2. 链表中删除选定结点的优雅操作!
  3. boot返回码规范 spring_sprigboot-new-coding-standards
  4. 计算机中函数counta表示,excel中counta函数的使用方法
  5. aMDcpu不支持mysql_Oracle 11.2.0.1在AMD CPU 64位硬件,32位操作系统下的BUG 8670579
  6. 怎么查看usb读取信息_电脑弹出USB设备时提示该设备正在使用中的解决方法
  7. python json.loads namespace_python json.loads兼容单引号数据的方法
  8. Python ——告白小程序,添加微信号(快来设置你的freestyle吧)
  9. 石油化工设备维护检修规程_旋回破碎机横梁臂架、衬板、内外铜套检修步骤及设备检修维护要点...
  10. php搭建的网站空白,使用phpstudy搭建dedecms网站后台页面空白解决方法