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以及偏移量,这里又分几种情况:

  1. 如果从服务器没传id或者id与当前主服务器不匹配,那主服务器将传送全量数据
  2. 如果从服务器的offset在缓冲区中不能找到(落后太多导致缓冲区已经被新数据覆盖了),那也会进行全量同步
  3. 如果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深度历险-复制的更多相关文章

  1. 分布式Redis深度历险-Cluster

    本文为分布式Redis深度历险系列的第三篇,主要内容为Redis的Cluster,也就是Redis集群功能. Redis集群是Redis官方提供的分布式方案,整个集群通过将所有数据分成16384个槽来 ...

  2. 分布式Redis深度历险-Sentinel

    上一篇介绍了Redis的主从服务器之间是如何同步数据的.试想下,在一主一从或一主多从的结构下,如果主服务器挂了,整个集群就不可用了,单点问题并没有解决.Redis使用Sentinel解决该问题,保障集 ...

  3. Redis深度历险——核心原理与应用实践

    高可用架构」的各位老铁们,你们好!你是否还记得上个月发布的文章中,有两篇深入讲解Redis的文章,分别是和,广大粉丝读者们对这两篇文章整体评价颇高.而我就是这两篇文章的原创作者「老钱」(钱文品),我是 ...

  4. 《Redis深度历险:核心原理和应用实践》千帆竞发——分布式锁

  5. Redis深度历险,全面解析Redis14个核心知识点

    本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取. 传送门: ...

  6. 分布式Redis主备复制

    当数据落在不同节点上时,如何保证数据节点之间的一致性是非常关键的 Redis采用主备复制的方式保证一致性,所有节点中,只有一个节点为主节点(master),它对外提供写服务,然后异步的将数据复制到其他 ...

  7. 《Redis深度历险:核心原理和应用实践》学习笔记一

    1.redis五种数据结构 1.1 String字符串类型,对应java字符串类型 用户信息序列化后,可以用string类型存入redis中批量读写string类型,见效网络消耗数字类型的string ...

  8. 【Redis深度历险】那些年Redis的数据结构

    [Redis深度历险]那些年Redis的数据结构 Redis端口号6379的来源 Redis的端口号是6379,但这个端口号并不是随机选择的,源于"MERZ",这个单词在手机当中的 ...

  9. Redis 深度历险

    学习资料 https://juejin.im/book/5afc2e5f6fb9a07a9b362527 包括下面几方面的内容 基础 应用 原理 集群 拓展 源码 to be done

随机推荐

  1. java基础第十九篇之Xml

    1:xml的概述 1.1 xml是什么 标记语言:语言中出现了<a></a>的标签 a:HTML 超文本标记语言 (语法非常严格,不能随意的定义标签) b:XML 可扩展的标记 ...

  2. Netty与RPC

    一.Netty原理 Netty是一个高性能.异步事件驱动的NIO框架,基于Java NIO提供的API实现.它提供了对TCP.UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都 ...

  3. 【win10】通过环境变量来快速打开应用程序

    step1:建一个空文件夹,并把文件夹路径复制到剪贴板. step2:依次右键点击“此电脑”.属性.高级系统设置.环境变量,定位到“系统变量”,点击新建. (说明:环境变量分为用户变量和系统变量,用户 ...

  4. 解决sublime快捷键回车换行问题

    鼠标右键sublime 以管理员身份运行 打开首选项里面的按键绑定用户 将下面的代码粘贴复制 { "keys": ["enter"], "comman ...

  5. 为Dynamics CRM注释的图片附件做个预览功能

    关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复163或者20151017可方便获取本文,同时可以在第一时间得到我发布的最新的博文信息,follow me! Dynamics CRM中注释可 ...

  6. python中动态创建类

    class Foo(Bar): pass Foo中有__metaclass__这个属性吗?如果是,Python会在内存中通过__metaclass__创建一个名字为Foo的类对象(我说的是类对象,请紧 ...

  7. Linux MySQL 开启远程访问

    进入mysql以后 use mysql; GRANT ALL ON *.* TO user@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;

  8. windows防火墙失效

    在某些情况下,我们希望阻止某款软件联网,比如防止软件更新. 通常来说使用windows自带的防火墙是可以阻止软件联网的,但在我的电脑上它却失效了,无法起到阻止软件联网的作用. 我的操作系统: OS 名 ...

  9. react界面跳转,滚动到顶部

    在使用react-router-dom时,我们经常会遇到路由切换时滚动到浏览器顶部的问题. 滚动到顶部 Scroll to top 很多时候我们需要的是滚动到顶部“Scroll to top”,因为发 ...

  10. python3.5.3rc1学习八:文件打包

    from cx_Freeze import setup, Executable setup(name='test to exe', version = '0.1', description='test ...