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

  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

原文地址:https://www.cnblogs.com/yuxiang1/p/11213381.html

时间: 2024-11-10 14:10:35

分布式Redis深度历险-复制的相关文章

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

Redis端口号6379的来源 Redis的端口号是6379,但这个端口号并不是随机选择的,源于"MERZ",这个单词在手机当中的对应数字就是6379."MERZ"在Redis作者Antirez的好友圈当中代表愚蠢的意思. 数据结构 Redis的key只能是字符串,value可以是String,Hash,List,Sorted Set(Zset). String Redis的字符串是动态字符串(SDS Simple Dynamic String ),内部结构有点儿类

redis分布式锁深度剖析

转自https://www.jianshu.com/p/d0caf5d99e56 redis分布式锁的实现主要是基于redis的setnx 命令(setnx命令解释见:http://doc.redisfans.com/string/setnx.html),我们来看一下setnx命令的作用: redis-setnx.png 1.redis分布式锁的基本实现 redis加锁命令: SETNX resource_name my_random_value PX 30000 这个命令的作用是在只有这个ke

细说分布式Redis架构设计和踩过的那些坑

摘要:本文章主要分成五个步骤内容讲解 Redis.RedisCluster和Codis; 我们更爱一致性; Codis在生产环境中的使用的经验和坑们; 对于分布式数据库和分布式架构的一些看法; Q & A环节. Codis是一个分布式Redis解决方案,与官方的纯P2P的模式不同,Codis采用的是Proxy-based的方案.今天我们介绍一下Codis及下一个大版本RebornDB的设计,同时会介绍一些Codis在实际应用场景中的tips.最后抛砖引玉,会介绍一下我对分布式存储的一些观点和看法

[转载] Codis作者黄东旭细说分布式Redis架构设计和踩过的那些坑们

原文: http://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&mid=208733458&idx=1&sn=691bfde670fb2dd649685723f7358fea&scene=1&key=c76941211a49ab58cb17c68ecaeeda0f1c083d9508a0f6629461fff9025fd87de4706bd9c1730e0ddbab70568b34b16a&ascene=0&

Codis作者黄东旭细说分布式Redis架构设计和踩过的那些坑们

本次分享的内容主要包括五个大部分: Redis.RedisCluster和Codis; 我们更爱一致性; Codis在生产环境中的使用的经验和坑们; 对于分布式数据库和分布式架构的一些看法; Q & A环节. ??Codis是一个分布式Redis解决方案,与官方的纯P2P的模式不同,Codis采用的是Proxy-based的方案.今天我们介绍一下Codis及下一个大版本RebornDB的设计,同时会介绍一些Codis在实际应用场景中的tips.最后抛砖引玉,会介绍一下我对分布式存储的一些观点和看

java和jre【java深度历险笔记】

<java深度历险>中介绍javac.exe是个包装类,其实javac.exe也是用java语言来实现的,那么它的运行是需要jre的支持的,所以 在java安装目录下有个jre,如图中所示,是为了用来支持javac等执行的. 那么java.exe用的应该是哪一套jre呢?可以自己指定.书中说明了这样一套流程 在执行命令java.exe时,[首先该命令的运行目录应该添加至PATH中,或者给命令添加绝对路径] 如图中红线所示,如果在下载完JDK后,添加path后就会出现java选项.如果将以上两条

Java深度历险(一) Jboss+EJB的第一个实例

学习感悟:每次学习新的知识,都会通过第一个小的实例入手,获得成就感,经典的Hello Workd实例奠定了我们成功的大门哈,这些经典的实例虽小但是五脏俱全呢,很好的理解了,Ejb的核心. 今天主要以这个小实例,来学习EJB的分布式,我们可以将接口与实现放到一台计算机上,而客户端可以放到另一台机器上,当然分布式的意思很大了(下篇博客给大家讲解),快来看实例. 一:代码实现 1:定义接口 <span style="font-size:18px;"><span style=

Java深度历险(三) Maven初步学习

这几天接触这个词,很多遍了,只是浅显的体会到它在GXPT中的好处,功能之强大,又通过网络查询了资料进一步的认识学习了,和大家分享. Maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具. 在gxpt中的体现 1. 父子关系 一个多模块项目通过一个父POM 引用一个或多个子模块来定义. a. 在父目录中分别创建子项目 b. 父项目,通过以下配置,将子项目关联: 如下: <span style="font-size:18px;"

SSH深度历险(四) Maven初步学?

这几天接触这个词,非常多遍了,仅仅是浅显的体会到它在GXPT中的优点,功能之强大,又通过网络查询了资料进一步的认识学习了,和大家分享. Maven是基于项目对象模型(POM),能够通过一小段描写叙述信息来管理项目的构建,报告和文档的软件项目管理工具. 在gxpt中的体现 1. 父子关系 一个多模块项目通过一个父POM 引用一个或多个子模块来定义. a. 在父文件夹中分别创建子项目 b. 父项目,通过下面配置,将子项目关联: 例如以下: <span style="font-size:18px