分布式Redis深度历险-复制
Redis深度历险分为两个部分,单机Redis和分布式Redis。
本文为分布式Redis深度历险系列的第一篇,主要内容为Redis的复制功能。
Redis的复制功能的作用和大多数分布式存储系统一样,就是为了支持主从设计,主从设计的好处有以下几点:
- 读写分离,提高读写性能
- 数据备份,减少数据丢失的风险
- 高可用,避免单点故障
旧版复制实现
Redis的复制主要分为同步和命令传播两个步骤:
同步可以理解为全量,是将主服务器某一时刻的所有数据全部同步到从服务器。
命令传播可以理解为增量,当主服务器数据被修改时,主服务器向从服务器发送对应的数据修改命令。
同步
同步分为以下几个步骤:
1.从服务器向主服务器发送SYNC命令(执行SLAVE OF命令的第一步也会执行SYNC)
2.主服务器在收到从服务器命令时,会执行BGSAVE,也就是新开一个子进程将内存中的数据保存到RDB文件中。同时使用一个内存缓冲区记录从现在开始执行的写命令,该内存缓冲区的作用就是记录RDB文件生成期间的增量。
3.向从服务器发送RDB文件
4.将缓冲区中的写命令发送给从服务器
同步可以分为两种情况,一种是从服务器第一次连接主服务器,另一种是从服务与主服务器的网络链接断开了,重新连上主服务器并重新同步。
命令传播
命令传播实现逻辑比较简单,当主服务器执行了写命令后,为了保证从服务器与主服务器数据的一致性,主服务器会将写命令发送给从服务器,从服务器执行完收到的写命令后其数据就能和主服务器保持一致了(当然会有延时),注意,从服务器对于客户端来说是只读的,因此从服务器的所有数据都是来自于主服务器的同步or命令传播。
旧版复制存在的问题
假设Redis主从服务器之间的网络环境不太可靠,我们来看看上述复制方法会出现什么问题。假设有主服务器A和从服务器B,主服务器中目前存在1-10000共一万条数据。
1.初始连接,从服务器第一次从主服务器同步数据,同步完成后,从服务器也有1-10000共一万条数据。
2.主服务器新增10001,10002两条数据
3.通过命令传播,从服务器也新增10001,10002两条数据
4.这时候主从服务器之间的网络断开
5.主服务器新增数据10003,因为网络断开,所以从服务器感受不到数据变化
6.网络恢复,从服务器重新连接上主服务器,并发送SYNC命令,进行同步操作
7.主服务器将所有数据发送给从服务器(1-10003)
从上述步骤中可以看到,当从服务器重新连接上主服务器时,会重新进行全量同步,造成大量不必要的IO开销,如果网络环境不稳定时,会导致主服务器一直将内存中的数据写到磁盘再发送给从服务器。
新版复制实现
为了解决老版复制问题,Redis2.8对于复制功能进行了优化。实现如下:
1.主服务器会维护一个偏移量,每次向服务器传播N个字节的数据时,该偏移量就会加上N,比如说一开始是0,接受到一条set key1 value1后,其偏移量就为13(真实偏移可能不是13,只是举个例子)。//这里可能要看下代码确认
2.从服务器也维护一个偏移量,当从服务器收到到主服务器的N个字节数据时,该偏移量会加上N。
3.主服务器维护一个固定大小的缓冲区,每次接受到客户端写命令后,都会将对应命令往这个缓冲区写入。当写入内容超出固定大小后,会覆盖原来的数据。
4.主服务器有一个唯一id
5.从服务器连接上主服务时,会向主服务器发送上一次连接的主服务器的id以及偏移量,这里又分几种情况:
- 如果从服务器没传id或者id与当前主服务器不匹配,那主服务器将传送全量数据
- 如果从服务器的offset在缓冲区中不能找到(落后太多导致缓冲区已经被新数据覆盖了),那也会进行全量同步
- 如果offset能在缓冲区找到,则主服务从offset开始,将缓冲区的数据依次发送给从服务器。(有做pipeline的优化吗)
以上就是新版复制的大致思路,要注意的是,主服务器缓冲区的大小设置很关键,如果设置的太大会导致空间浪费,如果太小会导致网络环境不好时,其退化为老版复制。
之前我就踩过这样的坑:在上云时,redis集群在两个不同机房,主从之前网络环境不太稳定,而redis机器上存储的value比较大,很容易就将缓冲区占满导致每次全量同步,形成恶性循环,从服务器落后不可读,主服务器不可写(当从Redis落后太多时,主Redis将拒绝写入,具体参数可以配置的,下文还会提到)
所以建议将缓冲区大小设置为平均重连间隔*每秒写入数据量*2
主从心跳机制
从服务器默认会每秒一次的频率向主服务器发送心跳:REPLCONF AÇK <replication_offset>,
replication_offset代表从服务器当前的复制偏移量。
心跳有三个作用:
1.检测主从服务器的网络连接
2.实现min-slaves功能
3.检测命令丢失
检测主从服务器的网络连接
主服务器会记录从服务器上次发送心跳是什么时间,根据这个时间,我们能知道主从服务器之间的连接是不是出现了故障
实现min-slaves功能
Redis为了保证数据的安全性,可以配置当从服务器小于min-slaves-to-write个或者min-slaves-to-write个从服务器的延迟都大于等于min-slaves-max-lag时,主服务器拒绝写。
检测命令丢失
主从之间的复制,其实是以主服务器作为从服务器的客户端来实现的(在Redis中,所有服务器之间的数据传递都是以该种方式)。假设主服务器向从服务器发送一条写命令,但网络出现异常,从服务器并没有收到该命令。
这就会导致数据不一致的状态(你可能想主服务器发送命令时,如果从没返回失败,进行重发不就好了吗?如果说从成功执行了命令,但是再回复主的时候出现了问题,那主如果重发就会造成数据异常了)。所以主服务器会根据心跳信息来决定要发送的数据。看个例子:
初始,主服务器和从服务器偏移量都是100。
主服务器收到客户端的写命令,将偏移量改成110,同时向从服务器发送写命令,但因网络原因,从服务器并没有收到,其偏移量仍然是100。主服务器根据心跳发现从服务器的偏移量是100落后于自己,所以会将100-110的数据进行重发。
看到这里,你可能对于上述方案的正确性感到质疑:在从服务器接收到100-110的数据前,它发送心跳包告诉主服务器自己当前偏移为100,然后接收到了100-110的数据。这时下个心跳还没发出,主服务器认为从服务器落后于自己,再次发送100-110的数据,导致从服务器再次写入100-110的数据,导致数据异常!
如果你有想到这个问题,说明你是有在认真思考了~
其实是不存在这种情况的,原因是redis是单线程的!记住单线程三个字,再回头看一遍问题描述,相信你能想明白~
原文:Java架构笔记
免费Java高级资料需要自己领取,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并发分布式等教程,一共30G。
传送门: https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q
分布式Redis深度历险-复制的更多相关文章
- 分布式Redis深度历险-Cluster
本文为分布式Redis深度历险系列的第三篇,主要内容为Redis的Cluster,也就是Redis集群功能. Redis集群是Redis官方提供的分布式方案,整个集群通过将所有数据分成16384个槽来 ...
- 分布式Redis深度历险-Sentinel
上一篇介绍了Redis的主从服务器之间是如何同步数据的.试想下,在一主一从或一主多从的结构下,如果主服务器挂了,整个集群就不可用了,单点问题并没有解决.Redis使用Sentinel解决该问题,保障集 ...
- Redis深度历险——核心原理与应用实践
高可用架构」的各位老铁们,你们好!你是否还记得上个月发布的文章中,有两篇深入讲解Redis的文章,分别是和,广大粉丝读者们对这两篇文章整体评价颇高.而我就是这两篇文章的原创作者「老钱」(钱文品),我是 ...
- 《Redis深度历险:核心原理和应用实践》千帆竞发——分布式锁
- Redis深度历险,全面解析Redis14个核心知识点
本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取. 传送门: ...
- 分布式Redis主备复制
当数据落在不同节点上时,如何保证数据节点之间的一致性是非常关键的 Redis采用主备复制的方式保证一致性,所有节点中,只有一个节点为主节点(master),它对外提供写服务,然后异步的将数据复制到其他 ...
- 《Redis深度历险:核心原理和应用实践》学习笔记一
1.redis五种数据结构 1.1 String字符串类型,对应java字符串类型 用户信息序列化后,可以用string类型存入redis中批量读写string类型,见效网络消耗数字类型的string ...
- 【Redis深度历险】那些年Redis的数据结构
[Redis深度历险]那些年Redis的数据结构 Redis端口号6379的来源 Redis的端口号是6379,但这个端口号并不是随机选择的,源于"MERZ",这个单词在手机当中的 ...
- Redis 深度历险
学习资料 https://juejin.im/book/5afc2e5f6fb9a07a9b362527 包括下面几方面的内容 基础 应用 原理 集群 拓展 源码 to be done
随机推荐
- XAF-内置初始化数据 (XPO)
Open the Updater.cs (Updater.vb) file, located in the MySolution.Module project's Database Update fo ...
- CTF丨Linux Pwn入门教程:针对函数重定位流程的相关测试(下)
Linux Pwn入门教程系列分享已接近尾声,本套课程是作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的题目和文章整理出一份相对完整的Linux Pwn教程. 教程仅针对i386/a ...
- 在linux上安装postgresql数据库
#postgres useradd postgres chown -R postgres:postgres /media su postgres mkdir -p /media/Data1/postg ...
- 【转载】每个 Android 开发者必须知道的消息机制问题总结
Android的消息机制几乎是面试必问的话题,当然也并不是因为面试,而去学习,更重要的是它在Android的开发中是必不可少的,占着举足轻重的地位,所以弄懂它是很有必要的.下面就来说说最基本的东西. ...
- App iCON 尺寸
120*120 180*180 58*58 87*87 80*80 120*120
- java annotation使用介绍
还望支持个人博客站:http://www.enjoytoday.cn 介绍 Annotation的中文名字叫注解,开始与JDK 1.5,为了增强xml元数据和代码的耦合性的产物.注解本身并没有业务逻辑 ...
- Angular 学习笔记(二)
控制器: 就像 JavaScript 里的构造函数一般,用来增强作用域(scope),当一个控制器通过 ng-controller 指令来添加到 DOM 中时, ng 会调用该控制器的构造函数来生成一 ...
- 一行代码完成 Java的 Excel 读写--easyexcel
最近我在 Github 上查找一个可以快速开发 excel 导入导出工具,偶然发现由阿里开发 easyexcel 开源项目,尝试使用后感觉这款工具挺不错的,下面分享一下我的 easyexcel 案例使 ...
- str基本语法
基本数据类型(int,bool,str)1.基本数据数据类型: int 整数 str 字符串. 一般不存放大量的数据 bool 布尔值. 用来判断. True, False list 列表.用来存放大 ...
- excel制作田字格,excel行高磅,列宽1/10英寸;
打开一个excel表格,发现列宽是行高的4倍: 开始-格式中查看,发现行高14.25磅,列宽8.38*1/10英寸: 网上百度,了解1英寸=72磅: 那么列宽8.38=60.336磅: 60.336英 ...