堪称货拉拉混合云数据库建设史,一场DBA团队的逆袭之战

1,030 阅读51分钟


本文根据
蔡鹏老师在**〖2021 Gdevops全球敏捷运维峰会-广州站〗现场演讲内容整理而成。**

讲师介绍

蔡鹏, 前饿了么,蚂蚁金服技术专家,现任货拉拉数据库部门负责人,负责货拉拉混合云化业务场景下整体数据库,消息队列,缓存,数据库中间件的稳定性建设工作。

分享概要

一、混合云数据库治理背景介绍

二、治理面临的挑战

三、痛点&诉求

四、治理基本思路 

五、平台整体架构

六、MySQL平台化建设

七、Redis平台化建设

八、Redis ServiceMesh建设

九、Kafka平台化建设

十、ES,RabbitMQ,Canal平台建设背景 

十一、研发自助化平台建设

十二、从运维走向运营 

十三、近一年治理成效 

十四、云时代下对DBA的思考

货拉拉作为一家业务遍及多个国家及地区的物流公司其面临的技术环境是非常复杂的,在云时代的浪潮里基于混合云快速构建环境搭建系统成了我们必然的选择。但是在混合云上如何对基于各家云构建的系统进行有效的管理是个挑战,尤其对处在系统最底层的数据库层面临的问题,业务诉求,各方痛点,是我及我的团队重点解决的。

一、混合云数据库治理背景介绍

从内容上我们可以看到,我们面临的治理环境其实是非常复杂的,几乎可以用头大来形容。由于历史上的种种原因,数据库选型众多。\

主要原因是DBA不够强势被研发主导,研发想怎么来就怎么来,DBA也没有替代的解决方案跟合理的拒绝理由,同时也有语言选型上的Bug:PHP。PHP几乎是所有问题的万恶之源,不仅仅体现在DB上,还为我们整个技术中心的服务治理埋下很多的坑,此处省略10万字!致敬这款伟大的语言默默地滋润了货拉拉这么多年

同时在部署上也缺乏统一规范,有直接使用云服务的,也有通过ECS自建的。整体管理水平非常弱,整个DBA团队当时也非常弱势被研发拖着走,故障频发。

二、治理面临的挑战

  • 第一次使用云,对云的套路不熟悉踩了不少坑,交了很多学费,比如:默认打开了某云数据库的回溯功能造成的额外成本问题;某云的续费方式差异造成的额外成本;某云的空闲代理使用问题造成无故的成本问题......这些问题都是我们在不断的对云的了解后发现的一些问题。这些冤枉成本足够给公司全体研发同学每顿都加只鸡腿。

  • 除了环境复杂外,很多数据库及中间件也是我第一次接触(也刷新了我对DBA这个职业的新认知,或者说扩展了我的技能树),这么多DB及中间件选型也很难有把握管理的都很到位。

  • 云商间的产品化差异也给统一治理&管控带来很大的挑战,比如实现分库分表可以使用阿里云的DRDS如果换到aws呢?又或者其他云呢?各家云产品解决方案是有差异的,这就给产品跨云移植带来很大挑战。

  • 面对当前这种多云环境+多数据库选型,如何解决多站点一站式运维管控+多云环境下一致性的运维与研发体验是有一定难度的。

多站点一站式运维管控:我不希望每个数据库及中间件都要单独做一个系统去管控然后各个站点都单独部署一套,这样做最大的问题是管理分散,DBA运维过程体感非常差,这是我非常不希望看到的也是绝对不能容忍的,我们要做的就是一站式的跨云多站点统一管控平台,虽然略微复杂但是用户使用体验非常好,技术本身就是把复杂留给自己简单留给用户,即使是内部系统自己使用也坚决不妥协。

多云环境下的一致性运维与研发体验:比如对DB来说,我希望经过系统对底层基础设施的差异做了屏蔽后会做一个统一的呈现而不是对各个云商产品进行针对性的设计系统,避免造成运维过程中的困惑。

对研发同学来说,他是完全不需要关心各家云商产品间的差异性问题,同时也无需考虑多云环境下研发日常涉及数据库的变更、资源申请、数据订阅等可能存在的差异性问题,这些复杂性由系统统一包掉。

三、痛点&诉求

1、稳定性

历史上日常故障率实在是太高了,隔三差五的故障,P2-3家常便饭,P0-1也非常多见。

2、研发效率

DBA完全靠人肉来满足研发诉求,研发跟DBA的交互完全靠吼,研发很多资源诉求、需求处理、问题排查都难以快速高效的满足。

不过这只是当初的表象问题,更大的问题在于如果这些问题不能快速得到解决后续随着业务的快速发展技术团队的规模快速增长,作为基础技术支撑团队一定会出现更多更严重的问题,这些都是可以预想到的毕竟经历过类似的快速成长的公司基本上会有相应的心理预判。

3、成本

在自建机房时代只要购买一次机器可以用上3~5年这期间可以不用一直付费,云上付费方式的差异可能会让老板觉得一直在花钱(毕竟每个月都有账单)。也许这是开玩笑不过当时是非常难以理解在一个快速成长的公司成本诉求来的太早了,这会对业务发展甚至稳定性产生一些掣肘。不过在一年后的今天来看确实是非常有必要的(我们在平台化后通过初步的数据分析后发现资源使用不合理居然普遍存在,我们在DB上节约的成本就相当可观,后续会介绍)。

不管是面对研发还是DBA自身又或者是老板各种的“不讲道理”都有不小的压力,但这又是不得不解决的问题。

四、治理基本思路

1、做减法

从一个我看到的现象说起,我注意到研发有高频的订正数据的需求,正常我想这是不应该的,除了个别的误操作及运营特殊需求不应该有这样的需求。

后来经过了解后发现,这样的一个现象,比如:由于订单逻辑的复杂性,一部分数据在落库时写MySQL,一部分数据要写Mongo或者ES或者Redis,用这些产品本身是没问题的,但如果打包到一个业务逻辑里面就有问题了,总会出现类似无法保证事务一致性的问题。当然里面有业务使用姿势的问题,但是另外一个非常重要的原因是,业务在设计之初缺乏合理的设计,或者说为了简单滥用了数据库的某些能力。比如为避免关系型数据库DDL麻烦的问题,就想着用Mongo来代替,最终也是一个挖坑操作;另外一方面,很多数据库选型是我们hold不住的,不是不能投入时间去研究,而是在有限的时间要解决最关键的问题。

结合我过往的经验看,还没见到一家公司的业务复杂到需要近10款数据库类型才能支撑的。警惕研发的肆意的、缺乏大局观的数据库选型,DBA有权利say no

因此,砍掉一些数据库选型尽管有不理解但也要坚决推行,DBA也不再接受研发过去的套路,(通常的台词是:系统已经开发完毕了,明天要上线...)找回DBA丢失的话语权,也是给DBA对外打交道上建立一点自信,当然减少数据库选型也一定程度上降低了后续平台化的复杂度,毕竟时间有限。

2、定义规范

过去是完全没有规范的,或者错误的规范,同时我们要明确DBA的职责及SLA保障标准,目的就是告诉研发:该做什么不该做什么。(比如研发过程使用某种我们不支持的数据库则不准上线!)及各种要遵循的规范化的内容。

DBA提供SLA保障标准也是对DBA严格要求,通过建立规范标准,让DBA有“法”可依,最起码也是起到保护DBA的作用。规范的建立如果只是文字性的内容往往是没有意义的,必须是能写进代码里,同时也要做好系统收口才能良好的执行,否则就是一纸空文(只有在跟产研打官司的时候有用,但是不希望发生这种情况),因此平台化是非常必要的。

3、建能力

如何通过平台化方式解决当下的问题,或者落地具体的治理手段。

4、70分标准

我们没有非常充足的时间去追求完美,优先解决核心的问题,做到差不多就行了,然后解决另外一个核心问题。也就是小步快跑,绝对不在某个不完美的点上磨磨唧唧,留在后续在不断的迭代优化、稳步向前推进。但是也要争取做一个功能就成功一个,否则面对这么多功能要做,总是失败会打击自己跟团队的自信,先取得一个个的小目标再计划后续。

5、优先解决生存问题

DBA很长一段时间里陷入到对研发人工支持上,比如:经常忙到很晚的发布,日常的资源交付,协助研发线上问题排查,各类研发工单处理等。

这些日常事务极大的消耗了DBA的精力,DBA长期挣扎在这些日常事务处理上,无暇他顾,形成恶性循环。如何通过平台化手段解决这些问题,让DBA从泥潭中抽身?这是所有问题中的最优先要解决的。

五、平台整体架构

1、技术栈

应该算是相对主流的技术栈选择,语言选择上没有优劣,只要自己掌握的了都不影响实现结果。只是不同语言特性的,可能会比较适合解决特定场景下的问题。比如我们要做Agent,选择Python显然就不是一个特别好的选择。

2、功能层面

主要面向跟DBA跟研发,为了方便DBA能随时随地处理问题,做了自己的小程序。比如在地铁里能做一下工单的审批及其他常规操作需求,或者躺床上盯盘等,提升工作效率跟幸福感。研发能自助解决日常的大部分问题,避免跟DBA有过度交互。

3、统一网关

一般简易的单点平台是不需要网关层的,一个Django就解决了。由于我们要通过一个平台解决多节点管控问题,我们平台服务层(Service-API)都是微服务化多站点部署的,此时微服务化的下统一网关就显得很有必要。前端跟后端交互时只要在Header里面带上Region信息即可正确的路由到指定的服务节点。

平台在设计之初就考虑到多站点的统一管理问题,我是非常不希望看到一套系统在各个站点反复部署的,使用体验简直不要太差!这里是稍微介绍一下为什么需要统一网关如图:

这种微服务化的架构在部署跟实际使用过程中对DBA及研发体验都非常好。\

1)数据总线:

主要用于数据(监控metric类数据非业务数据)传输使用,比如从最远端的站点A到集中管控系统部署站点的网络延迟在600ms如果A站点的监控数据要往管理站点上报要通过网络直接上报就显得很困难。

为了减少过多的Kafka部署增加运维的复杂性我们其他站点的监控数据统一上报到一个网络距离上处在相对中间的kafka集群,然后在通过mirror的方式将监控数据同步到管控节点进行集中的消费处理,整体看监控数据从最远端的站点传回到集中管控节点通常控制在1s内整体可以接受,其实最主要的是监控数据写到Kafka避免监控数据因为网络原因导致丢失。

2)平台组件集:

其他组件后续会陆续介绍,这里简单介绍一下任务调度:

DBA日常工作中肯定会有很多定时任务要维护即使平台化了也仍旧需要,过去DBA是将这些任务通过crontab进行管理,但是这样是很局限的,比如获取执行状态、结果、日志比较麻烦,存在单点问题,脚本分散化不利于维护,年久容易失修一但被遗忘里面的脚本逻辑中可能会产生破坏作用,存在安全隐患,为此我们单独实现了一个调度管理系统对散落在各个站点上的任务进行统一集中管理。

调度本身实现是比较简单的可以理解成crontab的网络版,只是在调度本身的基础上添加了一些管理模块如:节点注册,通讯模块,心跳检测等,如果不清楚crontab实现原理的可以搜索Github有比较多的实现参考方式。其实功能还是比较简单的只是实现了调度的基本功能跟分布式部署,时间关系(70分标准)并未来得及将节点实现集群化避免调度单点问题比如某一个调度节点down机其他节点会自动接管。

3)添加一个任务:

4)注册一个脚本:

5)任务管理:

通过调度系统很快就完成收拢散落在各个站点上的crontab任务。

六、MySQL平台化建设

1、监控报警

1)健康大盘:

通过对数据库近50个监控指标的权重打分,得到一个数据库实例的健康度,Dashboard直接按照健康度排序,即可一眼看透当前所有实例的健康状况。同时,在做权重打分时也会顺带将有问题的地方做好描述,方便DBA直接通过系统一眼定位问题。大致是这样的:

当然,为了方便DBA能对问题进行快速深入的分析处理,我们几乎将DBA所有常用操作,都封装到了监控大盘的快捷操作中。如:SQL快照(包含用户、状态、时间、锁信息等),实时抓取SQL信息、Kill SQL、SQL限流/熔断、报警屏蔽等。日常工作中,尽量避免DBA通过命令行登录到数据库实例进行黑屏操作,这个是我们形成共识。不管多么资深的DBA一定在命令行模式下犯过一些低级错误!!因此平台化要能覆盖到这些微小的点。

2)报警:

我们数据库的报警量还是比较多的(每天2-500条左右),这也是我们做的不够好的地方。回顾我经历过的一些公司或者了解到的一些公司,几乎都是有类似的问题,甚至每天收到上千条报警的也不在少数。报警看似是非常简单的,但是要做好其实是非常困难的,既想报警少,又想核心报警不能漏。有时大家为了减少报警,甚至想干掉报警本身,而不是是去想着解决问题,这是本末倒置的。

其实很多报警是可以不用发出来的,因为发出来也没有人去进一步处理,或者被自愈系统自动处理掉了。但是种种原因考虑又不得不发。在治理水平跟不上时,宁愿多报也不能漏报(漏报关键报警信息是非常严重的,如果有问题则罪加一等)。

报警多其实反应的是治理水平没有跟上,有些问题看似是非致命性问题,但是如果长期不去处理,问题最终会被放大甚至恶化成故障。我们的做法是:将报警数据化统计分析呈现每日趋势,重点盯住报警趋势线,安排专人跟进,将趋势线控制在合理的范围。从统计数据上解决面的问题,而不是点对点的解决某一个问题。

2、运维管理

此前,数据库的基础信息一直是存放Excel里面的,所以很难想象这种Excel式运维有多痛苦!需要通过系统方式进行有效管理,只有这些基本元数据信息有效的管理起来,后续功能才能依次展开,比如集群包含的实例信息,集群与集群组间的关系、数据库与集群的关系、数据库内的对象信息等等。有了这些基本信息,再对其打上各类运维需要的标签信息,方便做更多细粒度的管理工作

运维中很重要的一个工作就是发布,尤其是在公司高速发展阶段,产品功能快速迭代自然就涉及大量的发布工作。为了减轻DBA的负担,让研发不再受制于DBA,人力问题我们需要将它自助化。

研发可以通过系统自助完成发布工作,这里比较麻烦的是:如何做好用户间的权限隔离,避免误发布?

这里需要结合公司SSO+系统元数据管理,其中,系统元数据的有效性是整个系统能否做成功的最关键的因素。 我们不能依靠DBA来维护元数据信息,必须依靠完善的系统机制保证,做到元数据接近准实时状态。比如DB跟用户组织结构的关联问题,我们采用类似协商的机制,来保障它们间的绝对准确。

比如A先认领了数据库DB1,B用户也来认领该数据库,如果它们同属一个组织结构,则B的认领是合法的,否则会被系统拒绝。后续如果A或者B组织结构发生调整,则涉及该数据库的发布操作将被禁止,用户必须内部完成协商:要么带走这个数据库(别人取消认领),要么留下这个数据库(自己取消认领)。类似的场景在系统里面有多处,如果依靠DBA来管理这些变化是非常困难的,因此元数据的自闭环管理是整个系统最底层的逻辑,元数据必须是100%可靠的。 很多系统比如CMDB,之所以难做或者很少有公司CMDB能做好,个人理解,很重要的一个原因就是在这里没做好。

发布系统实现上其实是有很多的复杂性的,不仅仅是业务逻辑复杂,比如Sharding表跟普通表如何区分?Sharding表要怎么处理才能保证效率跟安全问题?大量DDL任务下发到执行服务器(DDL执行节点)如何保证执行节点不被打挂?等等。

我们基于gh-ost改造实现DDL,DDL过程会有非常大的Binlog解析动作,如果并发控制不好,会导致执行节点网卡被打爆。

这些都是比较复杂的问题。当然整个发布流程还涉及很多基础组件的设计开发工作,比如SQLReview、gh-ost、Rollback后续篇幅将依次介绍,发布系统使用简单但是开发过程确是一个系统性的工程有比较大的工作量,我们目前研发自助发布率几乎接近100%,DBA不用再忙到半夜搞发布。

3、应急/自愈

数据库需要应急的场景,最多的就是:SQL把数据库打垮了。此前,我们是通过操作阿里云的管理后台实现限流,但这个方法只能使用于阿里云数据库,我们还有其他云数据库选型,而且操作阿里云限流效率不是特别的高,要登录到控制台,然后还要根据出问题的InstanceID,找对应的实例,再找到管理页面操作......而且没有提供SDK操作不够便利。

还记得我们前面一直提到的:提供一致性的运维与研发体验吗?作为平台研发就要考虑通用的解决方案,比如通过我们自己的中间件,来实现统一限流,这样就不存在云上产品差异的问题,内部系统操作上也便利,也有SDK接口跟我们的DMS系统对接。

还有诸如冒烟事件处理,自愈系统会实时检测系统存在的异常,比如:未提交事务、锁等待、连接数飙升、长查询、SQL执行堆积、CPU飙升等,系统会实时检测并针对性的启动相关处理规则,将冒烟扑灭,避免冒烟演变成火灾。

4、数据统计分析

通过平台的任务管理部署采集脚本每天对系统表:tables,table_io_waits_summary_by_index_usage,table_io_waits_summary_by_table进行采集分析,我们可以清楚的知道:当前那些DB/Table的冷热度情况。每张表的每个索引使用情况(那些未使用),为后续治理提供数据支撑。比如,随着业务的迭代,很多库跟表已经被遗弃了,但长期存在于数据库中,DBA又不能删除清理掉。尤其在云上,这就涉及资源闲置跟成本浪费。

即使出于DBA的洁癖,也应该及时识别出这些数据进行清理。(这些残留信息存在的越久则治理越腐朽。)

慢查询是相对简单的功能。但是由于各家云上产品差异,我们系统对每家云产品特点做了针对性封装。比如阿里云,我们直接通过SDK获取,AWS则要下载文件到本地化,然后进行分析然后在统一呈现,这还是比较麻烦的,我们目前已经放弃该方案。所有DB都已经接了数据库中间件,所有的SQL都通过旁路的方式落库,数据库中间件对SQL打有足够多的标签描述,不仅仅能反应出慢查询的情况,还能根据全量SQL做更多分析工作。

由于SQL量非常巨大,要想存储起来分析是很难的。我们采用了折中的方案,中间件会根据SQL指纹对SQL采用聚合,这样落库的SQL数量就呈现指数级下降,便于统计与分析。

5、DB上云与自建的差别

上述功能其实都不涉及传统自建时代,围绕基础设施做的一系列的建设工作,功能本身更多是聚焦业务功能本身。因此,开发过程中相对还是轻量的。如果是自建,则要考虑的问题非常多,从硬件、系统、数据库、HA等等,都是非常复杂的。

过去DBA在这块积累的大量的经验,每个资深DBA可能在这块,都有专属于自己的黑科技,这也是DBA过去比较有价值跟难以替代的地方。但是随着DB的上云成为趋势,传统的做法正在成为历史,也逐渐的被新入行的DBA所淡忘,云对基础设施的封装是时代的进步。

回想10年前入行的时候,那时还是11k/15k的SAS盘,还在折腾什么场景该用Raid10还是Raid5?是Write Through还是Write Back?不同机型配置下my.cnf该怎么设置?OS内核调参、不同数据库版本下特性有什么不同?尤其复制的改进,对实现HA起到什么影响?HA该怎么选做?是MMM还是MHA、ORC还是自己做一个HA系统?如何快速安装部署、快速搭建主从?备份是物理还是逻辑备份等等,这些随着技术的进步及云的进一步渗透,正在成为远古记忆。

6、MySQL中间件建设

自建中间件其实是有很多考量因素主要集中在这3点:

1)统一数据库保护层

前文提到混合云下,云产品的差异性不同,不同云产品对数据库保护机制不一样,也不开放SDK。此外,由于云上实例规格普遍都是小规格,一般都是购买4C/8C这样规格。这样的小规格下,应对异常情况下的抗压能力其实是非常差的。

跟自建时普遍32C的规格比,可能一个执行计划不是非常理想的查询,就可以导致CPU被打满,导致数据库进一步恶化加速。

为此,我们建设了自己的数据库中间件,提供统一的保护机制。中间件有一个SQL执行队列,一但遇到数据库性能恶化RT增加队列就会堆积,中间件就会感知到数据库的响应异常,会主动地启动保护机制。此时,DBA监控系统也会感知到DB的异常情况,借助DMS的快速分析处理能力可以快速定位具体的SQL,紧接着就可以启动针对SQL的限流或者熔断机制,保证数据库的安全。

2)数据库可扩展问题

即使在云上,数据库规格也是有限的,货拉拉运营这么多年,加上近两年订单不断的翻倍,累计了几百TB的数据。这些数据很难通过单实例的方式存储。虽然过去货拉拉也做了分库分表,但是支撑非常困难。

这些开源的中间件问题比较多很难被hold住,距离一款商业化的产品还有比较远的距离,因此一个开源产品如果用于核心场景,必须是能hold住的,否则出问题那叫一个干着急啊。

云上目前已经有很多分布式数据库,或者可以水平扩展的数据库选型,但还是不能被我们直接使用。一方面不够熟悉,另一方面,各家云商产品标准不一样,最后就是价格太贵。何况我们也还没到海量数据的程度,杀鸡无需用牛刀;而且引入一款新的数据库,考虑因素太多了,如果只是单一云环境尝鲜新,数据库选型也可以考虑。但是多云环境下,按照云商的推荐去搞会导致数据库选型泛滥,最终会制造无限的麻烦,还是尽量选择能被完全把控的方案相对稳妥。

可以预见,未来企业肯定也是以混合云为主。如何做好产品选择是很重要的,一定要考虑好产品间的兼容性与业务系统程序的可移植性,避免跨云移植水土不服。不兼容是必然存在的,作为核心基础设施的我们,在能力建设上要充分地考虑跟对应的能力建设工作。

3)多语言统一访问层

创业型公司语言选择往往是很混乱的:PHP、Python、Go、Java可能都会有。几年前在饿厂的时候就听过这样一句话:“语言的选择可能会决定一家公司的成败”。

比如货拉拉以PHP为主,它的整个生态,或者语言局限给后续上规模化后的管理、运维、服务治理带来很多问题。这种小语种甚至都找不到靠谱的开发,更别提其生态建设了。

比如在数据库上,普遍采用短连接有的核心服务就有几百个,Pod高峰期数据库连接数大几千个,这对于小规格内存只有16G/32G左右的实例来说实在是太重了。接入中间件后,连接数直接能从5000降低到300,这还是非常可观的。这里顺便说一句:之所以缓存既选择了Codis又有哨兵,还有Cluster,跟PHP都有一定关系,其中PHP业务就是Codis为主(所以前面吐槽PHP的原因就在这里,当然吐槽的不仅仅是数据库,还有服务治理上的...)。

为了适应PHP的长期存在我们也在对Redis进行Mesh化建设与治理暂且按下不表。

同时的在数据库的深度可观测性上,可以做更多工作,过去对热点SQL、SQL分布、RT分布是很难有合适的手段的(虽然后续有了PS功能,但还是显得很局限),通过中间件旁路,这些信息可以轻松获取到。

有了中间件的加持+DBA服务治理+平台建设,数据库的稳定性有了长效的保障机制。中间件的建设也为解决一致性运维与研发体验提供一些支持。

7、MySQL基础工具建设

自愈系统:

系统会实时感知到数据库负载情况,结合数据库中间件的能力快速作出决策进行限流或者SQL查杀。同时,基于云的弹性能力进行自动扩容,云上都有类似的ModifyDBInstanceSpec接口,比如检测到空间不足则立即扩容,因此我们可以将实例空间维持在一个很高的使用水位,尽量成本合理化。

1)SQLReview

目前在业内有很多开源的解决方案,但是我还是坚持自己做了一个,理由也很简单:可以自由灵活的个性化定制。不仅仅覆盖面要全,同时也融入DBA经验性的内容,可以基于统计数据做公司内的“大数据”分析,清楚地知道:大家整体容易在什么地方犯错?哪些团队做的好?哪些团队做的差等。同时,自研一个因为完全能hold住,跟其他自研系统可以无缝对接高度的灵活。此外还针对性地对货拉拉过去规范上的错误或者不足,做出识别与纠正,如果基于开源就不太容易做到,或者有一定改造成本。

SQLReview核心模块就是SQL解析,这个参考了TIDB的实现模块,有兴趣的可以看下其核心解析模块:githup.com/pingcap/tidb/ast,githup.com/pingcap/tidb/parser,不过这块随着源码的不断提交会有一定的差异。如果我们一直跟着官方最新代码包去编译,容易产生非预期问题,建议是本地化包管理或者干脆把这个解析模块单独扣出来。

关于审核规则,一方面充分吸收了开源系统的的一些规则,还融入了一些DBA经验理解及过往错误规则纠正:

这些会在研发提交到发布系统前进行统一的校验。

2)gh-ost建设

gh-ost(相信很多人已经对他很熟悉了)只是一个单纯的DDL工具,如果跟平台进行整合还是要做一些改动的,以适应我们多站点化的部署:

根据统计我们平均每天的DDL量超过200+,如果是sharding一个逻辑表的DDL,会被拆分成1024个DDL,如果这些DDL由一台机器来完成是非常困难的,而且研发在提交发布的时候,可能都集中在发布窗口自动打开之后的一段时间内。因此,即使在同一个站点内,执行节点也需要部署多个,每次向执行节点分发任务都会根据执行节点的任务数来做均衡分发。之所以还需要在每个执行节点维护一个执行队列,主要是因为gh-ost本身导致的。简单回顾一下其基本原理:

由于其通过拉取并解析Binlog来代替PT的触发器机制,因此,当对Binlog产生量比较大的实例进行DDL时,网络带宽会比较高。当多个任务同时进行时,带宽可能会被打满(在云上DMS使用的ECS,EC2网络配置都不是特别高基本都在4C8C、2Gb5Gb带宽),同时CPU也会很高。

主要是gh-ost要对记录做循环的逐行逐列处理,系统高负载下会导致DDL失败的概率增加。所以在每个执行节点上都加入了一个任务队列,通过线程池做稳妥的并行化控制,避免相互影响。

此外,还通过网络模块对其进行良好的控制,比如优雅终止,还有日志的阉割(其日志实在是有点啰嗦),由于比较简单不做赘述。

3)Flashback(DML回滚)

我们并没有将回滚内置到DMS发布系统里面,主要还是嫌麻烦,毕竟实际需要回滚的场景还是非常稀少的,内置到发布系统做起来有点重。当然好处是需要用的时候很快捷,我们是在gh-ost执行过程中埋点关键点位信息:start binlogfileend binlogfile,start posend pos,ThreadID ,有这几个关键点位信息,根据ThreadID可以精确获取对应变更的BinlogEvents然后生成逆向SQL。其实很多开源的系统也是这么做的,所不同的是,直接根据Binlog QueryEventData里面的Threadid,一边DML一边进行Binlog解析,然后拼装成对应的逆向SQL保存,当需要的时候直接执行。

很早以前,对误操作或者闪回DBA也是伤透脑筋,甚至想出了各种各样的黑科技做法。比如以正则表达式为代表的脚本工具类的居多,但是这些都是不靠谱的。随着对MySQL的进一步深入及开源化建设(或者说生态逐步完善),这些实现变的越来越廉价,适当的掌握一定开发能力可能都会轻松实现。

不过,仅仅是基于一下开源的堆砌,是难以做到合理的平台化整合,只有深入理解或者对其代码逻辑结构有比较多的理解,然后在此基础上进行深入改造融合,才能用起来比较趁手。

我们MySQL主要还是围绕云服务来进行建设,但是我们还是做了大量的基本能力建设工作,不是说上云了就是万事大吉,还是有很多事情要去做。云解决了基础设施的SLA保障问题,但是业务层面的问题还是需要我们自己来解决。

我们的系统完全由DBA自己开发完成,从前端到服务端到架构到各个数据库平台具体功能细节,DBA基本已经不是单纯的DBA了。在云时代下,DBA需要更加的多元化的能力模型,入门门槛很低(会操作就行),但是要求确非常高(全栈工程师的要求),很多新手DBA在云时代下越来越难以接触到数据库完整的生命周期管理(从硬件->软件->资源交->HA...)更多越来越偏向业务本身,因此相比过去的老一代DBA,他们缺少了一点点底蕴,不过新手DBA一定不要陷入一个误区:以为会操作就是玩转了数据库,殊不知当前之所以能玩得转,可能是因为构建在当前运维管理体系下的,如果有一天脱离了这个体系,自己是否还能hold得住。

七、Redis平台化建设

由于很多因素,我们Redis服务是基于云ECS自建的。Redis本身的运维其实是非常简单的,没有MySQL那么多复杂的东西,在基础功能上涵盖了DBA日常用到的绝大部分功能,功能实现上也没有特别多的复杂内容。

1、监控大盘

出于个人喜好的原因,Redis沿袭了MySQL的大盘套路:

总的来说,就要达到一眼定位问题,同时将所有DBA排查定位问题,日常操作等功能都集成进来。比如:

2、实例替换

可以快速完成实例的迁移替换,及某一台机器的快速替换。主要用在服务器内存超卖策略下,某一个机器内存不足或者单个实例过大时的便捷操作。由于我们整个DB、缓存、队列的资源交付统一是资源ID的交付模式,原则上底层资源变动对上游业务无感:

研发不用关注数据源的配置是什么,只要将资源ID跟APP ID建立绑定关系即可访问到数据。

3、扩缩容

扩缩缩容的背后,系统维护了一个资源池。我们会常态化地保持池内有一定的闲置资源,确保随时可以使用。

4、全量Key分析

Redis运维过程中,非常重要的事情就是:做好Key的分析与监测,比如Big key。此前,我们做这个功能的时候,是在每台机器上部署任务,定时将RDB保存到共享存储里面,然后进行集中分析。这种方案非常的笨重低效,在Agent开发完善后我们做了全面的改进:

Agent内置了RDB分析功能,同时也有网络模块提供对外调用,由于我们一台ECS上部署了多个节点,如果Agent同时分析,会对服务器有一定的入侵,比如会占用一定的内存资源。为此,每个Service都内置一个调度模块做任务调度协调与任务分发,避免集中分析对服务器的侵入。我们对RDB的分析本身也做了很多细致工作,比如我们在通过开源工具分析RDB时发现:对于比较大的RDB文件,分析时会占用非常多的系统内存,这是不符合Agent低侵入要求的。我们通过Agent做超过20G的RDB分析,Agent整体内存也始终控制在100MB以内。

Agent基于Go实现无其他依赖,每台DBA交付的ECS/EC2上默认会部署并启动Agent,Agent会主动报告:我是谁(IP)、在哪里(region:区域/节点)。因此,基于Agent我们可以做很多自动化运维的事情,比如自动化的安装部署等。

由于历史原因,选型上有云、Redis、哨兵(占比80%)、Codis、Cluster,出于统一运维标准的考虑,统一采用Cluster模式,因此其他选型到今天已基本被淘汰。Cluster在可扩展性上会有更多的优势,也是未来的主流方向,但是我们还有很多PHP的服务,从Codis改造到Cluster后出现了很多其他的一些问题:

  • 连接压力会比较大。主要是一些高频的CLUSTER SLOTS请求导致clusterReplyMultiBulkSlots占用大量CPU资源,整体性能消耗比较高。
//默认配置$redisList = [    'tcp://127.0.0.1.1:7000?timout=3.0',     'tcp://127.0.0.1:7000?timout=3.0',     'tcp://127.0.0.1:7000?timout=3.0',     'tcp://127.0.0.1:7000?timout=3.0',];//通过绑定slots解决$redisList = [    'tcp://127.0.0.1:7000?timout=3.0&slots=1-100',     'tcp://127.0.0.1:7000?timout=3.0&slots=101-200',     'tcp://127.0.0.1:7000?timout=3.0&slots=201-300',     'tcp://127.0.0.1:7000?timout=3.0&slots=301-400',....];
  • Java客户端不统一对pipline支持,有差异,同时也在Lettuce重连机制上多次踩坑。云上ECS都是跨AZ的,云上有时会遇到网络抖动情况,一但出现网络问题,Lettuce就会有问题。详见:www.debugger.wiki/article/htm… 为此,我们框架层对Lettuce重连机制做了改动,最终解决这个问题。

  • Cluster这种多节点的集群模式,会多分配很多内存资源跟服务器资源,同时我们经过统计50%的实例,使用率都低于1GB。集群内存使用率非常低(我们最小规格为3M3S 3GB内存资源)。再加上Slave,是有很大程度的资源浪费的。

总的来说,在Redis的基础运维上全面实现Cluster化后,我们当前的方式是没有太大问题的。但是在进一步的深入治理上,我们做的还不够的。因此,我们也正在做我们的Mesh化建设。

八、Redis ServiceMesh建设

所谓Mesh化其实就是将Proxy本地化内置到客户端服务器,即sidecar模式。Local Proxy可以充分利用客户端资源,缩短链路,减少云上跨AZ带来网络上的不确定性。我们前面在Codis上也出现过客户端跨多AZ跟Proyx间网络问题,导致部分Client有影响。

1、服务&成本治理

  • 多语言统一访问层:这里跟前面讲的数据库中间件性质类似,不做赘述。

  • 多租户:前面介绍过Cluster模式下的天然资源浪费,单纯依靠运维手段是难以很好的解决的。我们为了解决这个问题,引入了多租户的概念,即一个Cluster集群在Proxy层进加一个逻辑划分既:namespace,同时,基于user/pasword的认证,用户在读取写入时会给每一个Key强制加上一个Key前缀,避免Key冲突:

但是这也还是有缺陷的,虽然集群内部Key是可以避免冲突的,但是没办法做到资源的隔离,而只是逻辑的隔离。我们将一些小容量的Cluster,集中迁移到统一的大集群中进行租户划分,尽管无法做到资源隔离,但是通过适度的资源冗余,也能很好的避免性能问题。同时,由于混部集中会有效减少资源浪费,根据我们现有的数据测算,可以节约40%~60的内存成本,同时有效减少现有集群规模跟节点数量。

  • 在可观测性上,通过Proxy对Key的旁路与分析可以实时感知到大Key(虽然通过Agent可以每日分析,但是这个实时性比较差会漏掉已过期的Key)、热Key、及每个命令类型的rt分布情况。

  • 辅助治理:比如可以实时检测到非法命令的使用及拦截、同时每个Key都有特定的用户标签,遇到大Key、热Key比较容易定位所属研发。

  • 研发友好:统一的pipline,通过Proxy本地化、热Key缓存等。

  • 副作用:Key长度增加

九、Kafka平台化建设

1、Kafka治理背景

1)一种开源工具拼凑的管理系统

  • 部署在各个IDC,管理分散,功能弱,不成体系

2)管控能力弱,碎片化监控

  • 消费延迟监控不直观,单纯Offset Lag没有意义

  • Broker监控要额外部署相关Export及监控报警系统

  • Topic相互之间资源竞争,多次出现一些高流量的topic将整个系统资源打满影响其他topic的正常使用。

3)集群内部对象状态缺乏感知

  • consumer、topic、partition等核心对象无收集统计,无法主动感知业务异常

  • 核心对象缺乏基础监控数据无法做沉淀分析,运维靠人肉经验指导 

  • 问题定位缺乏必要的数据支持,服务稳定性完全依靠人工死磕 

4)平台化建设缺乏有效参考

  • 业内开源系统稀少,没有可以直接使用的

  • 可观测性不够友好或者目前只对Java会比较好

2、Kafka平台

1)集群管理

此前我们都是在各个站点进行分别部署对应的开源管理系统,管理比较分散,现在得益于现有架构的实现,我们可以轻松实现一站式管控

2)Topic管理

除了对Topic的常规操作封装到平台外,还通过对Topic元数据信息落库,我们可以在全局的角度分析一些问题。比如当前哪些Topic是热点Topic?那些Topic msg body比较大?(可能需要DBA找下业务方确认合理性,很多数据来源是Canal->MySQL->Kafka,研发为了简单可能会将全字段写入Kafka),那些Topic体量比较大等等。

①Topic详细信息:

通过对每个Topic的消息大小采样,可以知道:消息体大小分布情况,通过采样消息分析消息体大小突变的原因,方便作出优化处理。

②Topic分区信息:

同时对Topic的分区分布情况进行展示(实现对kafka对象信息的完整覆盖,完全实现了Kafka-manager的全部功能,篇幅原因不做详细介绍)

同时为了方便研发&DBA临时查看消息的诉求,也提供基础的查询能力。

2)Consumer管理

系统将Topic跟consumer的关系进行采集、保留,方便运维需要。

①Consumer Lag:

一般的Kafka延迟通常以Lags来衡量,但是Lags最大的问题在于难以做时间维度的量化,以至于难以判断延迟的真正程度,也不方便提供研发订阅(研发需要知道自己消费延迟情况,但是Lags对研发不够直观友好)。

比如上图,Lag=250k表达出来的意思就不如延迟时间=600s来的直观,基于时间的延迟实现,我们在提供研发做报警订阅时就有了直观的衡量标准。

但是受到Kafka HW及consumer延迟commit的影响,要获取consumer的精确延迟时间是不可能的。以上图为例,这是典型的大数据消费处理的特征:消费一批数据后在做commit,这时我们观察到它的延迟时间跟commit时机有非常大关系,呈现出了周期性波动。但是实际上,延迟的真实时间是小于600s的。而受到上述机制的影响,我们没有办法做精确计算。

如上图,我们可推测该consumer消费时就是典型的生产业务消费特征:逐条或者小批量消费后立即commit。我们看到它的延迟就相对稳定、(生产与消费比较稳定无明显的周期波动)波动在正常范畴内。\

②计算延迟时间思路:

  • 基于msg body时间戳法:

delay~= (hw offset msg body).timestamp-(consumer offset msg body).timestamp

通过获取两个offset点位的消息体中的时间戳做减法的方式,效率比较低理论上准确度好一点。

  • max(分区延迟Lags/平均分区生产速度)

非常高效但是准确度稍差,目前主要采用这种方式。

说明:实际实现要稍微复杂一点点,两种方案都会遇到很多细节要处理,篇幅原因不在细节上面面俱到。再次申明这是不精确的,只是相比Lags要更加可度量,方便做报警配置及研发订阅告警。

3)Broker管理

我们对Broker做了zone分组,方便实现后续的资源隔离。

3、元数据网关

目前研发消费Topic时只需要拿到Topic所在集群地址即可消费,这样就给我们管理带来一定问题同时也不够安全。

前面提到,我们现在所有的资源交付都是通过资源ID进行交付的,研发也必须只能通过资源ID才能访问到Kafka,因此我们网关就基于这个点进行建设:用户的consumer必须在DBA交付的资源ID中建立Topic跟consumer的绑定关系,用户在拿资源ID时需要对其指定的password、Topic跟consumer进行绑定关系验证。(框架对Kafka Client做了封装)

否则就拿不到资源ID,当然这个实现肯定是不完美的,聪明的研发肯定能想到办法绕过。比较理想的做法就是将绑定关系内置到Kafka server中,但是目前还没有精力进行这方面的投入。

4、多租户隔离

我们目前Kafka使用呈现出了两极分化的趋势,一个是超大规格的Topic,另外就是小规格Topic。尤其在高峰期时,网络带宽非常高,单机网卡流量能打到7Gb+。这影响了其他小规格的Topic业务正常使用。

我们知道在云上CPU、网络、存储资源都是比较贵的,而Kafka这种特性,决定了我们可以选择CPU规格略低一点,但是网络、存储要求比较高。不过遗憾的是,云上CPU、网络、存储是存在比例关系的。有时为了获取更大规格的网络跟存储资源,需要购买规格更大的机器。这样就造成了资源上的浪费,同时Topic间也相互影响。

我们对集群的Broker划分成多个zone,每个zone里面一般由3~5个Broker组成:

每个zone可以根据Topic的场景、业务场景或业务线来进行分配,比如zone_1可以分配给风控业务的大规模数据处理使用;zone_2则分配给一下小的业务场景混合使用。同时,正对zone_1的业务场景,我们分配给它高配的机器,zone_2我们分配给它低配置的机器。

这样一方面做到资源隔离,另一方面做到资源的合理使用,最大化发挥机器能力。但这样似乎还是不完美。比如:当zone里面有Topic规格发生变化,需要升级配置或者降低配置的时候,就会对现有zone的资源进行调整。为此我们也是有解决方案:依靠监控,我们可以清楚的知道Topic的及zone中Broker的容量情况的,当资源不足时,我们可以加新的Broker,或者将Topic调度到其他zone中。

目前平台基础功能已基本覆盖了开源的Kafka-manager,同时我们也对比较好的开源实现思路整合进平台。而且我们也做了很多的数据沉淀,为我们更好地治理提供依据。

十、ES,RabbitMQ,Canal平台建设背景

难以想象,这些中间件的管理系统加起来有几十个之多!难以忍受!

ES,Canal,MQ其自身的可观测不是特别好。不过好在它们都再带了自己的Web-admin,这就给实现集中式管理提供了可能。除了我们通过分析起Web-admin、API将其关键元数据缓存之外,我们DMS平台的诸多功能都是对现有Web-admin的API的调用实现,一种非常取巧的实现方式。同时,元数据的采集也对我们更好地维护治理这些基础中间件,提供更好的依据。

1、ES

对集群有了更多运维属性的标签便于维护

通过DMS对索引添加研发属性描述,便于后续维护

资源申请与统一交付

索引轮转统一管理,减轻研发心智

2、MQ

一个完整版的MQ-Admin,实现管理从分散到集中化的管理,操作便捷高效。

3、Canal

完整版本的CanlAdmin

流程化数据订阅:Canal->Kafka->研发消费,DBA高效交付,研发使用便捷。

关键监控点采集可视化提供统一预警。

十一、研发自助化平台建设

服务一个几千人的研发团队,如果依靠DBA人肉支持,后果是难以想象的。DMS满足了开发同学对研发过程中对DBA的诉求,所有的操作基本都可以通过平台化手段进行自助。DBA如何变被动为主动?平台化就是关键手段。

研发想要的我们都提供,同时也主动展示给研发一些开发过程中暴露出来的问题,方便研发改造。

包含MySQL、Redis、ES、Kafka的查询等。

十二、从运维走向运营

经过一年的马不停蹄,虽然数据库类型很多,但是我们还是基本上完成了每款数据库及中间件的核心功能的建设工作。

但是我们并没有考虑跟进一步走向所谓的智能化阶段,应该说,目前业内呈现的智能化还是一个很虚的概念,真正付出实践的太少了,或者大多数都是基于规则引擎的更高程度的自动化,对于一些体量不大公司基本上属于KPI驱动的产物。

对于当下的货拉拉,我觉得比较务实的举动就是实现运维的数字化运营。这是当前正在做的方向,其实也比较简单,就是基于平台的数据沉淀,对核心指标进行展示、体现出整体稳定性治理的趋势、云上资源利用率分布变化等等。

十三、近一年治理成效

经过一年的建设我们在稳定性上有了长足的进步,同时,基于平台化建立起标准化的运维体系。

我们一直说标准化与体系化,那么是什么标准化跟体系化?如何来践行?我个人的理解就是通过编程的方式将我们的标准规范流程固化到系统里统一接收外界输入或者对外界统一输出。

在这一年里我们以超越预期的速度进行疯狂输出,也很庆幸结果也都超预期,否则很难想象,今天可以轻松服务几千人的研发团队!

我们在平台化没有成型前,是没有数据支撑我们做成本的优化的,有了数据的支持,我们发现资源的浪费其实还是很严重的,好在云上都具备规格弹性能力,经过合理的缩容、资源合并、老旧业务下线改造等方式,在成本上有着非常可观的节约。

上述平台建设与治理内容,我们用了一年的时间走完了3年的历程。一方面有云的加持,另一方面我们也多了大量的投入。平台人员投入2.5人(我算半个)今天也就3个平台研发。

从前端React到后端Python、Go到框架到架构设计,几乎是全栈DBA,很多数据库选型我们都是第一次接触,但是丝毫不会阻碍进度,比如Kafka从0认知到完整的平台化也不超过3个月,ES、MQ也几乎是1~2个月就落地的。应该说我们不再是单纯DBA的定位,这种多元、综合的能力建立,是我对整个DBA团队后续的期望跟要求!

十四、云时代下对DBA的思考

云极大的简化了传统自建时代,围绕基础设施稳定性建设的复杂性,云已经成为了一种新的基础设施。

1、DBA职责转变

云上数据库的可靠性、稳定性在云能力的加持下,不再是核心工作,DBA将更加偏向业务,比如性能优化,成本优化等等。

我们在自建时代也会考虑成本,但是通常都非常难做到非常高的资源利用率,这里面有非常多的原因。近几年也看到将DB搬到K8s的做法,其实本质的就是想通过这样的手段来达到类似云的能力。

但是这个投入是非常大的,甚至超过了收益,也伴随很多的风险,非常不建议中小公司做这种尝试。个人也非常不看好K8s里面跑DB的做法(云原生才是未来),千万不要以为能将它跑起来就是能驾驭的了他。

2、能力模型改变

围绕自建打造的传统能力将没有生命力(还有比一个SDK来的更简单高效的吗),DBA在自建时代的很多“黑科技”将逐步失去用武之地。

过去DBA深入研究数据库内核,比如对MySQL的各种原理机制理解的非常透彻,虽然这些也还是非常重要,但是留给DBA的操作空间并不大,比如云上有规格跟参数模板严格的参数控制,基于开源理解的特性跟云上优化,或者改造后的并不完全一致,因此DBA深入理解云产品比深如理解数据库本身,可能更有帮助。

不过遗憾的是目前云产品本身的帮助文档还非常的不健全或者介绍的过于简单了。

3、上云误区

云上DBA仍旧有用武之地,仍旧有很多有挑战的事情要去解决。

4、转型思考

拥抱变化上云不可抵抗,不要把自己定位是一个DBA这太局限了(省略后续鸡汤)。

实际使用云的过程中从各家云提供的SDK已经高度模块化了,稍微有点编程能力就可以将这些SDK组装起来,拼凑成你要的样子,很大程度上降低了开发的复杂性。

不知道我们是否思考过一个问题:为什么每家公司,不管是DBA还是其他运维部门,都还在不遗余力的做平台?这些平台其实功能都是同质化的,或者说高度的重复性建设,虽然呈现形态不一样,但是内部的机制几乎都一样。我觉得未来这种局面会随着云上对运维标准的定义和建立而发生很大改变,或许以后我们不用再建立平台,而是直接使用云平台,或者采购三方开发者提供的标准化解决方案。

云正在重新塑造整个IT行业,作为从业者积极调整改变很重要,如果打不过他们,那就加入他们(云厂商)。