分布式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
随机推荐
- [转]Oracle 11g R2 RAC高可用连接特性 – SCAN详解
原文地址:http://czmmiao.iteye.com/blog/2124373 昨天帮朋友解决11g RAC SCAN问题,当时为这朋友简单解答了一些SCAN特性相关的问题,但我知道这仅仅是 ...
- Python格式化输出和深浅复制
字符串的四种拼接方法,常用格式化 deepcopy(深复制) bytes 和 bytearray 的基本用法 字符串拼接 使用 + 格式化字符串 使用join 用单引号内的字符来拼接,参数填一个整体 ...
- [Linux] 解决nginx: [emerg] directive "rewrite" is not terminated by ";"
解决nginx: [emerg] directive "rewrite" is not terminated by ";"nginx的rewite规则有时候没注 ...
- 平方,立方,n次方,上标/下标
选种你需要的上标,如2,右键设置单元格格式:选择上标,确定即可: 最终效果:
- linux学习(六)文件基本属性
Linux系统是一种典型的多用户系统,不同的用户处于不同的地位,拥有不同的权限.为了保护系统的安全性,Linux系统对不同的用户访问同一文件(包括目录文件)的权限做了不同的规定. 在Linux中我们可 ...
- 再次体会wireshark的威力!
今天一位同学给我打电话,说是重启了客户机房里的一台服务器A,重启之后此台服务器不能与B服务器通信了,要命的是A台服务器上有关键数据需要与B服务器进行通信交互.客户大发雷霆,同学的老板也发火一再催促要尽 ...
- monkey事件
一.操作事件简介 monkey所操作的随机事件流中包含11大事件,分别是触摸事件.手势事件.二指缩放事件.轨迹球事件.屏幕旋转事件.基本导航事件.主要导航事件.系统按键事件.启动activity事件. ...
- 剑指Offer-8.跳台阶(C++/Java)
题目: 一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果). 分析: 实际上就是斐波那契数列的一个应用,青蛙跳上n级台阶的跳法数等于跳 ...
- 设计模式-Prototype(通过复制构造函数实现自我复制)-(创建型模式)
以下代码来源: 设计模式精解-GoF 23种设计模式解析附C++实现源码 //Prototype.h #pragma once class Prototype { public: virtual ~P ...
- 基于C++的STL的vector实现静态链表,要求包含插入,删除,和查找功能
//main.cpp部分 #include"List.cpp" int main() { StaticList<int> SL; SL.Insert(,); SL.In ...