【专栏】核心篇07| Redis “jio”美的集群模式

news2024/11/24 7:50:05

关注公众号 【离心计划】呀,一起逃离地球表面 

Redis专栏合集

【专栏】01| Redis夜的第一章

【专栏】基础篇02| Redis 旁路缓存的价值

【专栏】基础篇03| Redis 花样的数据结构

【专栏】基础篇04| Redis 该怎么保证数据不丢失(上)

【专栏】基础篇05| Redis 该怎么保证数据不丢失(下)

【专栏】核心篇06| Redis 存储高可用背后的模式

前言

    前一节我们从存储高可用的角度讲了一下其背后的经典架构模式,数据库发展这么多年,发展到分布式、云计算、大数据的时代,单机一体化自然有些水土不服,随着Google的GFS、MapReduce和BigTable的发表,分布式计算与分布式存储方案遍地开花,分片式存储大环境下对于流量来说是负载均衡的体现,对于数据存储来说是Auto-Sharding的刚需,这一节让我们看看Redis在其发展过程中“jio”美的集群模式。

从主从到哨兵

    Redis的主从模式与我们上一节讲的相同,属于整库存储,主库负责写从库负责读,分流读写请求。

     但是为了如果出现了故障,就需要运维人员手动进行主从切换,这是很繁琐的,那么工程师们首先想到的就是让这个工作自动化,还记得上一小节我们提到的两种方式:双机互联切换、中间代理切换,而哨兵就是这个中间代理。

监控

    哨兵是一种特殊的Redis实例,只是在配置Redis上有所不同。哨兵作为Redis的故障切换工作者,主要有监控与切换两部分工作。监控主要是健康检测,哨兵会定时去ping主从,如果主库没有响应pong,那哨兵就判定这个主库挂了,判定为下线状态,然后完成主从切换。而哨兵本身也是以集群的方式提供检测与切换服务,所以有多个哨兵时就需要达成一个共识,共识有两方面:大家都觉得它挂了和自己觉得它挂了,这在哨兵集群中称为客观下线主观下线

    我们按照事前事中和事后的方法分析哨兵集群完成主从切换需要做的事情,首先事前每个哨兵实例需要不间断去检查主库和从库,由于哨兵配置时会指定主库,所以主库的ip与端口肯定知道,那么哨兵是怎么知道有哪些从库的呢?这其实是哨兵会向主库发送INFO命令,主库会返回从库列表给哨兵。

    事中则是当哨兵发现主库有问题时,会判定为主观下线,并且需要让别的实例知道,那么哨兵之间是怎么互相通知的呢?这就需要Redis的消息订阅机制了,消息队列的工作模式中就有订阅/发布模式,Redis中亦然。哨兵们会在主库上订阅一个频道,比如叫做__sentinel_info,那么哨兵可以在这个频道上发送自己上线的事件比如新来了一个哨兵,发送了自己的ip与端口信息,别的哨兵订阅了这个频道就会收到这个消息,就知道多加入了一个兄弟,这样哨兵之间就都知道了彼此的ip与端口,这也是为什么在配置哨兵集群时只需要在配置文件中写好主库的地址,就能知道互相通信的原因。

哨兵判断主库主观下线后会发送is-master-down-by-addr询问其他实例是否同意下线这个主库,别的哨兵收到后会根据自身判断情况给出yes或者no的响应。那这里又有一个问题,最终判断下线的标准是什么呢?哨兵很公平:少数服从多数,因此当一个哨兵收到超过一半的yes票后,就可以判定这个主库为客观下线了。

选主

    事后则是判断完是否下线后,就需要进行主从切换,那么第一个问题就是,谁来切换,这么多哨兵兄弟,总不能每个人去切换一遍。这又是哨兵中的leader选举过程了,当哨兵实例判断为客观下线后,就毛遂自荐,向别的哨兵表明我可以去进行切换,别的哨兵实例需要给这个自荐的实例投票,当自荐的哨兵满足超过一般的赞成票并且超过了配置文件的票数阙值才能进行主从切换,这其中还有一个规定,就是一个哨兵只能给一个自荐者投票,这样才能保证最后的唯一性。当唯一的一个自荐哨兵满足条件后,它就成为了leader去进行主从切换了,当然在leader选举过程中会由于各种问题导致选举失败,那么就会进行下一轮选举。

找到了谁来切换后,就有第二个问题,切换谁?这么多从库,我又得选举一个出来,真的麻烦(由此也知道故障切换是一个极其复杂且耗时的操作)。这次选举,就不是毛遂自荐的形式了,由leader哨兵独裁,那独裁总得有标准吧,到底哪个从库更优秀呢?哨兵机制是有一个评分标准的,专门用于leader选举,而评分的条件有:

  1. 根据已有配置优先级决定,属于人为因素,因为有些从库可能本身配置就比较好,运维人员就可以指定它的高优先级

  2. 与原主库同步进度。同步的越多说明数据丢失的越少

  3. ID小的从库优先级更高,这个数据兜底策略

有了这些标准,就可以找出一个哨兵去完成切换的工作了。

关注公众号 【离心计划】呀,一起逃离地球表面 

通知

    知道了选哪一个从库成为新主库后,leader就可以完成切换,并告诉其他从库新主库的ip与端口,从库会执行replicaof命令与主库建立连接,从库会进行数据复制,关于主从库之间是如何保持数据一致性的,我们会在其他小结讲解。那么对于服务端来说,主从已经完成了切换,那么如何让客户端知道新主库的信息呢?

    这又用到了Redis的消息订阅通知机制,这次的频道在每个哨兵实例上,哨兵在切换主从库后,会发送一个switch master事件到频道上,Redis客户端主动订阅哨兵该频道后,接收到这个事件就可以从中拿到新的主库ip与端口信息进行切换,当然这个过程可能就会有存在gap导致写入失败。

    以上就是哨兵集群的基本工作模式,哨兵通过监控、选主、通知完成了主从切换的任务,也叫故障转移。同时哨兵集群也存在一定问题,比如需要保证哨兵集群本身的高可用,需要维护额外的哨兵实例,当然还有就是哨兵集群中每个Redis实例存放全量数据,没有解决数据容量问题,所以哨兵是站在高可用的角度,解决数据容量问题还有切片集群的方案。

Redis Cluster

    上一讲我们说过切片集群的模式就是把一整份数据按照一定规则切分成多份数据,由多个实例去存储。Redis的集群方案在发展过程中主要形成了以Codis为主的第三方Proxy与官方的RedisCluster,分别对应了中心化去中心化两种模式。

数据分布

    在Redis3.0之前并没有切片官方集群方案,切片集群是数据分片存储的一种方案,而Redis Cluster是具体的一种实现。Redis作为键值数据库,在完成数据分片存储时需要知道这个key放到哪个分片,这个key从哪个分片去拿,这就涉及一个数据分布问题,Cluster的做法是将所有key分成16384个,每一个key根据其CRC16计算出16位的值后对16384取模后对应唯一一个槽。而槽与实例之间的映射关系,是在Cluster部署时就做好的,可以平均分配槽,也可以根据配置不同手动cluster addslots 添加槽,无论什么方式都必须完成16384个槽的全部分布。

    那么key与槽之间的映射关系完成了,客户端作为集群外的角色如何知道槽与Redis实例的对应关系呢?首先,当我们分配完所有槽后,Redis实例之间可以互相通信交换彼此的槽信息并在自己这里维护一个全量路由表,当客户端以CLuster方式与集群连接后,可以知道所有实例,客户端可以通过cluster slots命令获取这个路由信息,这样客户端就知道了槽与实例的对应关系。

    而每一个分片所在的实例,为了保证存储高可用,使用了双机架构中的主从模式,每一个分片下也包括一主多从,但是Cluster模式的从库是不承担流量的,只做数据备份,因此其实也可以叫做主备模式,但是这只是Cluster对它的定义,由于Cluster是去中心化的,读写分流其实做在客户端实现完全是可以的。

故障转移

    Cluster下当节点发生故障后如何进行下线或者主从切换?Cluster不像哨兵集群有人帮它,它只能通过内部实例之间不断彼此通信达到互相检测的目的。具体实现我们简单来说就是每个实例不断Ping别的实例,别的实例返回对应的Pong消息,这个过程遵循Gossip协议,当Pong的延迟超过了cluster-node-timeout阙值时候,当前实例就判断目标实例主观下线,然后会广播这个主观下线的消息,只有其余的持有槽的主节点才会进行投票,从节点没资格,超过一半实例判断主观下线后,就判断为客观下线了,然后Cluster就会进行下线或者切换处理,这个过程和哨兵集群处理方式大同小异。

    然后就是和哨兵一样的问题,选哪个从节点作为主节点。这里和哨兵处理流程一样,先是资格评定,由于没有哨兵,所以从节点要自评,如果与主库断开连接时间超过cluster-node-time*cluster-slave-validity-factor,则当前从节点不具备故障转移资格,cluster-slave-validity-factor设置为0代表任何slave都可以被转换为master,默认为10。

    满足资格后,根据数据复制的offset大小,设置一个定时器,offset越大表示复制程度越高,定时阙值越小,达到定时时间后触发选举,这样保证数据复制多的实例更早进行选举,而选举的过程和上面一样,让其余切片主库参与投票。

    最后得到唯一资格从库后,进行主从切换,然后广播给其余主库更新路由表,自己也更新一下,结束。

看到这了还不关注嘛,关注公众号 【离心计划】,一起逃离地球表面 

数据迁移

    当我们需要重新分布槽比如新加入了几台实例,Cluster会怎么做呢?根据官方文档(https://redis.io/commands/cluster-setslot/#redis-cluster-live-resharding-explained),主要有这么几个步骤

  1. 将目标节点设置为importing状态,将源实例设置为migrating状态

  2. 获取源实例该槽的key列表,用migrate命令分批迁移key,migrate包括dump、restore和delete三个命令,dump负责序列化对应的key,restore负责反序列化key重放命令,delete负责在源实例删除迁移的key。所以这三个命令组成的migrate是一个原子命令。

这个过程中,由于migrate是原子的,且是一个同步阻塞操作,因此如果迁移过程中存在bigkey,那么就会阻塞用户请求。

目前为止,客户端可以完成下面的几个步骤,但是当我们的槽信息发送变动时,比如将原来的槽110从实例A分配到实例B上去,这个动作如何让客户端感知呢?

  1. 对key进行CRC16取模获得槽索引

  2. 根据路由表获取槽对应的实例信息

  3. 向对应实例发送具体命令

这个问题本质上是一个注册发现问题,只不过我们之前的注册发现讲的都是服务,而现在是槽而已,由于上面我们说到Redis每个实例维护了全量的路由信息,因此实例对槽的动态分配是实时感知的,Redis将槽的动态发现放到了集群内部,同样我们以事前事中和事后的模板方法分析数据迁移这个问题,我们一样以槽110从A到B举例。

事前,一切正常执行,因为没有发生槽的变动。

事中,也就是正在迁移一个槽中的所有key,那么这个槽在实例A上处理进行中状态,此时如果有对槽110的key读写操作来了,实例A会返回客户端一个ASK,这个ASK包含了正在迁移的状态、实例B的地址信息;然后客户端拿到这个ASK信息后,先向实例B发送ASKING命令,这个命令的唯一作用就是打开客户端REDIS_ASKING标识,然后再发送具体指令,这个指令就会携带REDIS_ASKING标识到达B,B就知道了这个指令是经过重定向后来的,就返回具体信息,然后REDIS_ASKING标识就会被剔除,它是一次性的。

事后,已经完成了A到B的迁移,此时客户端的路由信息依旧没有更新,此时发送key到A后,A会返回MOVED,也是携带了B的地址信息,客户端拿到这个信息后会重写路由表,然后向B发送指令,这样下一次命中槽110的key就会直接发往B了。

这样,Cluster就靠自己内部的沟通完成了数据迁移并且保证了迁移过程中的稳定性,这也是去中心化存储的表现之一,通过内部元信息实现数据稳定。

喂喂喂过分了,到这还不关注,关注公众号 【离心计划】,一起逃离地球表面 

Codis

Codis作为中心化集群模式,主要包括了几个组件

  • codis f&codisdashboard集群管理工具,可以以Web的方式进行集群的管理,像server的变动、数据迁移等

  • codis proxy  转发请求到server,兼容了RESP协议,可以理解为特殊Redis,也可以完成读写分离

  • codis server  二次开发的Redis,增加了支持数据迁移的数据结构

  • zookeeper  集群元数据管理,包含proxy、server信息,以及数据分布信息

数据分布

Codis的数据分布和Cluster是差不多的,也是以槽为单位分配key,只不过这个分配过程可以通过Codis dashboard完成。另外在Cluster中槽与实例的分配关系维护在每个Redis实例中,而Codis由于其中心化模式,这些信息属于元信息,统一由zookeeper管理。然后proxy可以总zk获取路由表信息,当请求来的时候由proxy处理CRC取模操作,完成key到槽、槽到实例的映射关系,而这部分操作之前是由客户端自己完成的。

另外,Codis也是使用了主从架构,但是和Cluster不一样的是Codis的从实例天然支持读写分离,在上层Proxy就可以做掉。

故障转移

Codis的故障转移是借助了哨兵机制来保证,从上面Codis的架构图中也可以看到右上部分有Sentinel部分,这边不再阐述。

数据迁移

Codis的数据迁移有两种,分别是同步迁移和异步迁移,同步迁移的工作和Cluster差不多,都有bigkey的阻塞问题。而异步迁移重点就在于异步,我在RPC专栏中解释过同步异步、阻塞非阻塞等含义,异步面向的是服务端,接收请求的一端不马上处理请求而是直接返回,等完成后再返回真正结果,所以异步迁移的过程大家应该就很清晰了。异步迁移过程中源实例的这一批正在迁移的key是只读的,那我们自然想问如果写命令来了怎么办,还记得在将Redis数据持久化时,AOF的重写过程如果遇到增量命令怎么办,那时候Redis的做法是设立缓冲区对吧,在这里codis比较粗暴直接报错,交给proxy进行重试就可以了。

小结

这一节我们大致了解了一下Redis的几种经典集群模式,由于篇幅所限(其实是我写不动了),就到这了。但是还有很多可以拿出来讲讲的东西,比如往大了看从槽到实例映射其实是负载均衡的体现,去中心化和中心化的理念以及什么什么变更了客户端怎么知道其实是分布式中状态变更与通知的问题,所以后面有时间在番外篇中我们可以从更宽阔的角度看看Redis,毕竟专栏的目标就是,不止步于get/set。

参考文献

  1. https://redis.io/docs/management/scaling/

  2. https://codechina.gitcode.host/programmer/distributed-DB/5-Redis-Cluster.html#redis-cluster-%E6%8E%A2%E7%B4%A2%E4%B8%8E%E6%80%9D%E8%80%83

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/58237.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Tableau:详细表达式(LOD表达式)的计算过程

写在最前 很早以前写过一篇关于Tableau详细级别表达式的博客,具体可以参考https://blog.csdn.net/yeshang_lady/article/details/104945361。前序博客重点在于介绍三种详细级表达式,这篇博客主要介绍使用详细级别表达式时的注意事项。 1 LOD表达式与非聚…

毕业设计-深度学习的施工安全帽图像检测算法

目录 前言 课题背景和意义 实现技术思路 实现效果图样例 前言 📅大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越难,有不少课题是研究生级别难度的,对本科…

2022 年超详细过程步骤讲解 CentOS 7 安装jdk1.8

linux系统下安装jdk以及环境变量的设置、真的是比window下方便一万倍 1、卸载系统自带jdk 1.1 查看系统自带jdk java -version1.2 查看java相关文件 rpm -qa | grep java1.3 删除openjdk相关文件 我上边有四个对应文件 命令介绍: rpm     管理套件 -e   …

VMware=win’server2016=Hyper-V的使用安装

VMware=win’server2016=Hyper-V的使用安装 实验准备 VMware软件--win’server2016虚拟机一台 从外部本地计算机或U盘 上传/共享 一个映像文件进虚拟机 win2016虚

【Maven】Maven快速入门2022版:从概念、安装到基本使用

本期目录一、 简介1. Maven优点2. 强大的依赖管理3. 工作原理3.1 POM3.2 Maven模型二、 安装配置1. Maven核心程序解压与配置1.1 解压1.2 指定本地仓库1.3 配置阿里云提供的镜像仓库1.4 配置Maven工程的基础JDK版本2. 配置环境变量2.1 检查 JAVA_HOME2.2 配置MAVEN_HOME三、 Ma…

9、Springboot整合Swagger3

1.什么是Swagger? 我们在编写了大量的接口之后,如果接口的调用者不是自身的话,那么就会面临要编写接口文档的苦恼,这时候Swagger就应运而生了。 Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTf…

笔试强训(四十)

目录一、选择题二、编程题2.1 发邮件2.1.1 题目2.1.2 题解一、选择题 (1)上网的时候,访问某个页面却突然出现了某个运营商的网页,出现此问题可能的原因是(A) A.DNS劫持 B.DDos攻击 C.MAC地址欺骗 D.伪造DH…

springboot+zookeepr+dubbo的远程服务调用

首先一定确保zookeeper正常工作 公共接口模块 为了简化代码,可以把需要其他服务调用的接口,抽取为公共接口模块dubbo-common-service, public interface TestService {public String clientTest(); }服务提供方 导入dubbo、zookeeper依赖…

Python学习基础笔记十六——函数嵌套

1、函数的嵌套调用 def max(a, b):if a > b:return aelse:return bdef the_max(x, y, z): # 函数的嵌套调用c max(x, y)return max(c, z)print(the_max(1, 2, 3)) 2、函数的嵌套定义: a 1def outer():a 1def inner():b 2print(a)print(Inner)def inner2(…

【GlobalMapper精品教程】033:影像地图羽化方式详解

在Globalmapper中,可以很方便的对影响进行多种羽化值设置。 文章目录 1. 不要羽化此图层2. 沿一个或多个边缘羽化3. 羽化到有效数据的多边形覆盖4. 在当前选定的多边形内羽化5. 裁剪到选定的边界,而不是羽化6. 在多边形外部羽化,而不是内部加载配套案例数据包中的data033.ra…

Java基于springboot+vue的家用电器销售购物商城系统 前后端分离

开发背景和意义 随着人们生活的改变,家电基本在家家户户都存在,常用的家电例如冰箱,洗衣机,电视机,空调等设备可以让人们的生活方式变的更加快捷舒适,通常情况下人们购买家电的话都是到指定的家电销售场所…

带你全方位了解光谱共焦位移传感器

任何一个行业的产生和发展都离不开科学技术的进步,这也是市场需求量不断扩大产生的对行业的促进,促使相关人员对产品的不断更新换代,光谱共焦位移传感器的发展也是发生着日新月异的变化。 光谱共焦位移传感器原理: 一束白光&#…

vue基于 input 实现密码的显示与隐藏功能

目录前言:一、准备图标二、Vue程序界面的展示三、代码总结:前言: 大家都知道,一般情况下,输入框的密码我们是看不到密码的,只有当我们点击查看密码的小图标时,密码才会显现出来,实现…

3D深度相机---结构光

去年的仪器仪表的课有汇报,我还专门为3D深度像机做了个调研,一直用inter realsense的,最近老师让看结构光方案的,正好总结一下。 1. 结构光(Structured-light) 由于基于双目立体视觉的深度相机对环境光照强度比较敏感&#xff0…

台式电脑怎么格式化重装系统

我们都知道电脑重装系统默认是会格式化磁盘的,那么如果是自己选择需要格式化的其它磁盘需要怎么操作呢?一般我们都是在pe里面手动格式化磁盘,这样就能避免直接格式化导致的某些问题了,下面就来讲讲台式电脑怎么格式化重装系统吧。…

[信息系统项目管理师-2023备考]信息化与信息系统(二)

目录 一、软件工程 1.需求 1.1 需求分类 1.2 需求分析 2.UML 2.1 类图 2.2 对象图 2.3 构件图 2.4 组合结构图 2.5 用例图 2.6 顺序图 2.7 通信图 2.8 定时图 2.9 状态图 2.10 活动图 2.11 部署图 3.软件架构 4.软件设计 4.1 设计模式分类 4.2 能力成熟度模…

Docker:入门

目录 一、Docker简介 二、Docker安装及环境配置 准备工作 ①通过 uname -r 命令查看你当前的内核版本 ②使用 root 权限登录 Centos。确保 yum 包更新到最新 ③卸载旧版本(如果安装过旧版本的话) ④安装需要的软件包, yum-util 提供yum-config-manager功能&…

统计信号处理基础 习题解答6-11

题目&#xff1a; 考虑WSS中估计DC电平问题&#xff0c;给定 其中 是零均值WSS随机过程&#xff0c;ACF为 &#xff0c;估计A。建议在 时用图6.5所示的FIR滤波器的输出来估计A。注意估计量为&#xff1a; 输入 假定在n<0 时为零。为了得到更好的估计量&#xff0c;我们希望…

近邻法总结

目录1.最近邻法2.k-近邻法3.近邻法的快速算法4.剪辑近邻法5.压缩近邻法6.错误率分析1.最近邻法 算法思想 对于一个新样本&#xff0c;把它逐一与已知样本比较&#xff0c;找出距离新样本最近的已知样本&#xff0c;以该样本的类别作为新样本的类别。算法描述 2.k-近…

【MySQL入门实战1】-数据库三大范式

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&#x1f61…