本文整理自主题分享《美团数据库的高可用系统》,系超大规模数据库集群保稳系列的第一篇文章。对数据库而言,非常核心的就是如何保证其高可用性。本文围绕4个方面的内容展开,包括高可用简介、高可用部署、重点模块的设计思考以及对未来思考。希望能够对大家有所帮助或启发。
| B站视频:美团数据库高可用系统
在数据库集群规模迅速扩大的背景下,如果出现故障,如何快速恢复成百甚至数千个集群的数据和服务,是很多大型互联网企业面临的重要挑战。线上部署了几十万的微服务,数据库结构和拓扑随时在发生变更,系统重构、内核升级、硬件设备汰换、机房搬迁等等,也都会对数据库的稳定工作产生一定的影响。作为整个IT系统中最为重要、最为底层的服务,即便遇到了极小概率事件的冲击,也会造成非常大的影响。对美团数据库团队来说,”低垂的果实已经摘完”,我们开始着力应对这些小概率事件对业务造成的冲击。
数据库稳定性保障的破局之道:一方面是提升平均无故障间隔(MTTF),另一方面是提升应急响应能力,即缩短平均修复时间(MTTR)。在这个两个目标的指引下,美团数据库团队从能力驱动和故障驱动两个维度来构造整个稳定性保障的闭环体系。
从能力驱动的角度,我们借鉴了Google的稳定性保障体系。在最底部的三层,通过故障演练/预案建设、复盘、可观测性的维度,思考怎么缩短故障处理时长;中间四层更多的是围绕研发需求、设计、上线、变更管控来降低故障的发生概率;顶层是产品运营,即通过面向内部用户的运营,指导业务对数据库进行选型和合理的使用,不断提升产品和平台易用性,并针对业务特点提供相应的解决方案。
从故障驱动的角度来说,包含事前预防和发现,事中故障定位,事后恢复、复盘和改进等等。从事前、事中、事后的全生命周期以及软件开发的各个阶段,全面提升管控和应急响应能力。
基于过去多年保稳定方面的实践,本次沙龙将从如何提升进攻和防守能力,如何提升快速恢复能力,以及在进攻、防守、恢复形成闭环后,如何让人、系统、流程更好的协同和应对大规模故障几个方面,围绕数据库的高可用系统、数据库攻防演练建设实践、数据库容灾体系建设、数据库自治服务平台建设等4个议题进行介绍。希望能给从广大数据库从业者、业务研发人员带来启发和帮助。
首先分享下美团数据库高可用面临的问题和挑战,主要从3个层面进行展开:
第一个挑战是实例增长越来越快。下图1截取了2019年1月到2022年1月的数据,可以明显地看到实例规模的增长非常迅速,在大规模场景下,如何保证每一个实例的高可用性是一个非常大的挑战。大家都知道,保障几台机器稳定运行,跟保障几万台甚至几十万台机器的稳定运行,其复杂度完全不在一个量级。
第二个挑战是可用性(RTO)要求越来越严。美团业务类型偏在线实时交易,对系统可用性有非常高的要求,特别是即时配送要求更高。在业务发展的早期阶段,体量并发也不高,对系统可用性要求可能只有99.9%。但是随着业务体量快速增长,对系统可用性的要求就会不断增加,特别是比较偏底层的数据库系统,从99.9%到99.99%甚至更高。
第三个挑战是容灾场景的复杂性。容灾场景主要分成三个层面,第一个是常规容灾,比如日常软件、硬件或者网络故障;第二个是AZ容灾,即机房层面,如机房断网、机房宕机等;第三个是Region容灾,即更大空间容灾,典型的是城市级容灾,目前主要还在解决AZ级容灾,分如下五个阶段:
从图4可以看到,我们将AZ容灾分设第0至第4共5个阶段,简称L0-L4。随着等级的提高,场景越来越复杂,相应的规模也越大。从容灾规模维度看,单点->单个集群->某个业务依赖的集群->AZ内的集群,不同规模要求的能力是完全不一样的,除了规模之外还有容灾的场景也会在变化。
接下来,分享一下美团高可用系统发展历程。总的来说,美团的高可用发展历程是根据不同的阶段的矛盾和挑战,做出的相应解决策略和方案。到目前为止,有三次比较大的系统架构迭代:
后来根据这个想法落地的架构就是当前的高可用系统,是一个Raft Group,多节点、多机房部署。以前主要关注主从切换,但新系统类似八爪鱼,所有在集群里的节点都是统一托管,如主库、从库、Ripple(后续有说明)等;另一方面,它是一个高可用、高性能、大规模并发处理架构,我们部署了多个Raft Group分组来托管不同地域和服务等级数据库集群。除此之外,我们也正在思考新一代去中心化的新架构,将在文章的最后一个章节进行介绍。
这部分主要分两条线:控制流和数据流。
由下图6可看出,高可用组件分四个部分:HA Core是经典的3节点Raft Group部署(也可以是多节点部署);HA平台是管控系统,如主动切换、状态机查看、兜底等;HAservice是HA的API服务,负责跟外围系统交付;调度系统包括(Scheduler、Worker),是一个流程调度服务,处理状态机的轮转。元数据存储是外围依赖的核心服务,负责基础配置的管理,当配置变更后会通过它下发到所有的中间件节点,保证业务看到的是正常的数据库拓扑。
下面我们围绕4个高可用组件来展开介绍一下,高可用部署在应对AZ级容灾或Region级容灾的策略。
一是HA Core部署,它有三个特征:一是多Region,如下图6所示,以红色线为分界,左边是Region 1,右边是Region 2;二是多AZ,每一个HA Core是3AZ部署;三是多集群,每个HA Core的3节点集群会托管MySQL千级别实例。
二是微服务,有同步服务、调度服务和配置中心。
三是数据层,微服务除了配置中心,其他服务没有状态,因为状态都在数据层,数据层服务都是MGR集群,而且这些集群各自作用不一样,MGR是单Region写、多Region读,所以这里我们回到最开始说的Region容灾,这些数据需要做单元化或者隔离,现在这些集群还是单Region写的。
四是管控层,它是正常的平台服务,常规化的部署。
故障发现有两个核心指标:
先说漏判,如下图8所示的故障探测通道,每个探测通道完全独立,如普通探测通道,心跳探测通道、从库探测等完全独立决策,谁先发现都可以作为决策依据,现阶段这些通道都是在服务端主动并发发起探测。另外一方面就是业务端,访问中间件有业务端的报错,也纳入故障探测和决策,这个是业务视角的故障,不管什么问题只要业务达到有损阈值就可以判断决策,这部分还在建设中。综合多通道、以及客户端和服务端判定,哪一个通道发现了问题,直接开始做决策,不受其他通道影响,这是解决漏判问题的策略。
再说减少误判,我们引入了多节点协商机制,比如我们遇到某些问题会引入“民主”决策让大家发挥更多意见,最终汇总得出结论。HA Core是多节点,当每个节点独立探测,探测后也会独立去做判断和分析,如只有一个Follower节点认为故障是没用的,还需要Leader的分析并向其他Follower节点发起协商共同决策,只有大多数节点认为故障才会把它确定为故障,会注册故障并进行故障处理流程。图中的Backend DB是Raft Group每个节点各自独立的存储,各自探测的拓扑以及判定决策信息等都存在本地。
所谓故障选举一定是多个从库,一主一从不存在选举。美团的MySQL集群现状是1主多从,所以选举异常复杂,因为我们要保证容灾N+1、多AZ甚至多Region部署,选举时选谁做主库就非常重要,主要有两个影响因素:选举因子和选举策略。选举因子+选举策略 = 决定谁是新主。
(1)选举因子是影响选举的核心要素,一次故障有20多个选举因子会共同影响如何排序。
举两个例子,第一个是版本,一主五从的集群可能有三个版本,如果让最新的版本作为新主就会有问题,要尽量不让它作新主,因为如果新主的版本是最新的,但从库都是比他落后的低版本,就会出现一些兼容性问题;第二个是权重,假设其他因子都一样,但存在运维和例行维护的情况下会人为说把权重降低,或标记一个实例不能作为新主,那么权重100的比权重50的有更大机会成为新主。每个选举因子都有权重,最终做综合排序,如版本、binlog格式、权重、服务器配置等,综合选举因子的选举结果是1M(first master)。
但1M并不一定是最优,有业务是北上跨Region部署,如之前主库在北京,按照选举因子排序选出的1M到上海去了,显然是业务无法接受的,因为老主库在北京说明绝大部分业务部署在北京,一旦让它跨Region写入到上海,那么RT会增加很多,所以引入了选举策略。
(2)选举策略是同机房优先>同中心优先>同区域优先,是一个灵活的fallback策略。按照选举策略重新选举一个新主(称为2M),如果2M和1M是重叠的,就认为这个1M是满足业务诉求的Master,会将1M作为最终的主库。但有时候1M和2M的排序相差很大,这时我们尽量让那种没办法改变的因素以它为基础,把其它能改变的因素对齐,比如地域很难改变,但是要改位点等容易改变,最终权衡后选举出新主库。
举例:
这个例子说明最终选举出的主库是综合考虑后的排序,受各种因子影响。
为什么要保证数据一致?在我们现在这种规模的业务场景下,可用性优先策略已经没办法覆盖所有的业务场景,但是在主从架构下面,数据丢失又无处不在,参考图12为例,数据丢失的风险点比较多:
接下来,讲一下针对老主库宕机拿不到数据时的解决方案(这个叫策略S3)。在此,也参考MySQL主从库架构通过IO线程和SQL线程去解决一致性的思路。第一个难点,IO线程要尽量把数据拿全,这是非常关键的点,也是很难的事情;第二个难点,SQL线程能够快速把数据应用下去,达到这两方面才能保证0RPO。
第一个难点解决方案如图13所示,核心是Ripple,它是一个高性能的binlog订阅服务器,作用类似IO线程,保证快速的获取binlog。第一个特性是它的性能极高,因为普通从库IO线程除了处理binlog接收请求外,也处理很多比如写Relay log、一些锁逻辑处理,以及配合SQL线程一致性的工作,而Ripple的任务就是快速将数据全部拿过来而不用处理其它逻辑,binlog同步速度是普通MySQL IO线程的3倍以上;第二个特性是可以配置强一致,支持半同步机制,如果你认为可以牺牲RTO,比如可以牺牲3分钟但不能丢失数据,那么配置多副本的半同步超时策略(如3分钟),由于Ripple的性能高,实际超时时间远远小于这个值,退化概率非常低;第三个特性,它本身是一个存算分离的架构,即Ripple是轻量级容器+EBS云盘,用这样一种架构来保证数据完全。
第二个难点是事中从Ripple补数据,Ripple毕竟不是普通的MySQL,所以需要HA去兼容,但HA处理数据比SQL线程灵活很多,因为SQL线程在需要实时处理数据来保证一致性,而HA只有在故障期间才处理数据,可以拉长RTO做很多定制。还有这里提到的RPO≈0,为什么是近似,因为虽然可以做到RPO=0,但要接受RTO的损耗。如果你既要RTO,又要RPO,那么可以在策略灵活配置。
在目前业务规模下没办法兼得RTO和RPO,如果想要RTO就要牺牲一部分RPO或者反过来。本身里面有一些不可控因素会影响核心指标,比如事务的大小、延迟、完整性等,我们将这些配置通过策略开放给业务,让业务自己去决策。举个例子,如果你的业务有很多大事务,但你又要想0RPO,那么你就需要配置更长的RTO损耗。
常规场景,Leader故障后会在4秒之内快速选举一个新Leader出来继续工作,但也有一些场景,如下图14所示,MySQL Master和HA 的Leader都在AZ1,如果AZ1宕机之后怎么办?其实就出现右边这个图。老Leader在处理AZ1里的MySQL节点故障的同时,由于自身Leader也在AZ1,会中断老Leader的处理状态机并选举一个Leader,但新Leader并不知道老Leader状态机如何处理,就直接导致切换的失败。
多机房高可用的的核心解决思路是保证各个节点之间状态机的连续性。所谓状态机的连续性,就是Leader实时把状态机同步到所有Follower节点,一旦Follower节点重新变为Leader点后,它会继续老Leader的状态机,会有动作的临界状态判断。如下图15所示,它根据执行的代价判断是回滚还是继续执行,如果回滚,就会把老Leader状态机全部回滚之后从零开始处理;另外一方面,如果由于集群的拓扑和状态被破坏了,导致回滚状态机比较麻烦或回滚不了,那么就会继续执行老Leader状态机没有执行完的动作。
保证多节点状态机连续性:
由于配置服务是双Region部署,分为同Region下发和跨Region下发。同Region下发会写到存储层,而config-server会更新最新配置,并将配置并推送到客户端。跨Region下发,比如说北京、上海和深圳都有业务服务节点,最终把它推送下去的时候也会走一致性服务(Consistency-Server),即一旦某个Region更新后,我们会把数据推送到另一个Region里,Region之间的数据完全一致,如果另外一个的Region有业务服务节点,就会继续走同Region下发流程。
最后,分享一下对高可用未来的一些思考,主要包括以下三个方面: