[亿欧导读]
货拉拉作为一家业务遍及多个国家及地区的物流公司其面临的技术环境是非常复杂的,在云时代的浪潮里基于混合云快速构建环境搭建系统成了我们必然的选择。
题图来自“外部授权”
作者简介:
蔡鹏
前饿了么,蚂蚁金服技术专家,现任货拉拉数据库部门负责人,负责货拉拉混合云化业务场景下整体数据库,消息队列,缓存,数据库中间件的稳定性建设工作。
内容摘要:
货拉拉作为一家业务遍及多个国家及地区的物流公司其面临的技术环境是非常复杂的,在云时代的浪潮里基于混合云快速构建环境搭建系统成了我们必然的选择。但是在混合云上如何对基于各家云构建的系统进行有效的管理是个挑战,尤其对处在系统最底层的数据库层面临的问题,业务诉求,各方痛点,是我及我的团队重点解决的。
混合云数据库治理背景介绍从内容上我们可以看到我们面临的治理环境其实是非常复杂的,几乎可以用头大来形容。由于历史上的种种原因,数据库选型众多(主要原因是DBA不够强势被研发主导,研发想怎么来就怎么来,DBA也没有替代的解决方案跟合理的拒绝理由,同时也有语言选型上的Bug:PHP,PHP几乎是所有问题的万恶之源,不仅仅体现在DB上还为我们整个技术中心的服务治理埋下很多的坑,此处省略10万字!致敬这款伟大的语言默默地滋润了货拉拉这么多年),同时在部署上也缺乏统一规范,有直接使用云服务的,也有通过ECS自建的。整体管理水平非常弱,整个DBA团队当时也非常弱势被研发拖着走,故障频发。
治理面临的挑战1.第一次使用云,对云的套路不熟悉踩了不少坑,交了很多学费,比如:默认打开了某云数据库的回溯功能造成的额外成本问题;某云的续费方式差异造成的额外成本;某云的空闲代理使用问题造成无故的成本问题......这些问题都是我们在不断的对云的了解后发现的一些问题。这些冤枉成本足够给公司全体研发同学每顿都加只鸡腿。
2.除了环境复杂外,很多数据库及中间件也是我第一次接触(也刷新了我对DBA这个职业的新认知,或者说扩展了我的技能树),这么多DB及中间件选型也很难有把握管理的都很到位。
3.云商间的产品化差异也给统一治理管控带来很大的挑战,比如实现分库分表可以使用阿里云的DRDS如果换到aws呢?又或者其他云呢?各家云产品解决方案是有差异的,这就给产品跨云移植带来很大挑战。
4.面对当前这种多云环境+多数据库选型如何解决多站点一站式运维管控+多云环境下一致性的运维与研发体验是有一定难度的。
多站点一站式运维管控:我不希望每个数据库及中间件都要单独做一个系统去管控然后各个站点都单独部署一套,这样做最大的问题是管理分散,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有权利sayno),因此砍掉一些数据库选型尽管有不理解但是也要坚决的推行,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及研发体验都非常好。
数据总线:
主要用于数据(监控metric类数据非业务数据)传输使用,比如从最远端的站点A到集中管控系统部署站点的网络延迟在ms如果A站点的监控数据要往管理站点上报要通过网络直接上报就显得很困难。
为了减少过多的Kafka部署增加运维的复杂性我们其他站点的监控数据统一上报到一个网络距离上处在相对中间的kafka集群,然后在通过mirror的方式将监控数据同步到管控节点进行集中的消费处理,整体看监控数据从最远端的站点传回到集中管控节点通常控制在1s内整体可以接受,其实最主要的是监控数据写到Kafka避免监控数据因为网络原因导致丢失。
平台组件集:
其他组件后续会陆续介绍,这里简单介绍一下任务调度
DBA日常工作中肯定会有很多定时任务要维护即使平台化了也仍旧需要,过去DBA是将这些任务通过crontab进行管理,但是这样是很局限的,比如获取执行状态、结果、日志比较麻烦,存在单点问题,脚本分散化不利于维护,年久容易失修一但被遗忘里面的脚本逻辑中可能会产生破坏作用,存在安全隐患,为此我们单独实现了一个调度管理系统对散落在各个站点上的任务进行统一集中管理。
调度本身实现是比较简单的可以理解成crontab的网络版,只是在调度本身的基础上添加了一些管理模块如:节点注册,通讯模块,心跳检测等,如果不清楚crontab实现原理的可以搜索Github有比较多的实现参考方式。其实功能还是比较简单的只是实现了调度的基本功能跟分布式部署,时间关系(70分标准)并未来得及将节点实现集群化避免调度单点问题比如某一个调度节点down机其他节点会自动接管。
添加一个任务:
注册一个脚本:
任务管理:
通过调度系统很快就完成收拢散落在各个站点上的crontab任务。
MySQL平台化建设监控报警
健康大盘:
通过对数据库近50个监控指标的权重打分得到一个数据库实例的健康度,Dashboard直接按照健康度排序即可一眼看透当前所有实例的健康状况,同时在做权重打分时也会顺带将有问题的地方做好描述,方便DBA直接通过系统一眼定位问题。大致是这样的:
当然为了方便DBA能对问题进行快速深入的分析处理,我们几乎将DBA所有常用操作都封装到了监控大盘的快捷操作中。如:SQL快照(包含用户,状态,时间,锁信息等),实时抓取SQL信息,KillSQL,SQL限流/熔断,报警屏蔽等。日常工作中尽量避免DBA通过命令行登录到数据库实例进行黑屏操作,这个是我们形成共识,不管多么资深的DBA一定在命令行模式下犯过一些低级错误!!因此平台化要能覆盖到这些微小的点。
报警:
我们数据库的报警量还是比较多的(每天2-条左右),这也是我们做的不够好的地方,回顾我经历过的一些公司或者了解到的一些公司几乎都是有类似的问题,甚至每天收到上千条报警的也不在少数,报警看似是非常简单的但是要做好其实是非常困难的,既想报警少,又想核心报警不能漏,有时大家为了减少报警甚至是想干掉报警本身,而不是是去想着去解决问题,这是本末倒置的。
其实很多报警是可以不用发出来的,因为发出来也没有人去进一步处理或者被自愈系统自动处理掉了,但是种种原因考虑又不得不发,在治理水平跟不上时宁愿多报也不能漏报(漏报关键报警信息是非常严重的,如果有问题则罪加一等)。报警多其实反应的是治理水平没有跟上,有些问题看似是非致命性问题,但是如果长期不去处理问题最终会被放大甚至恶化成故障。我们的做法是将报警数据化统计分析呈现每日趋势重点盯住报警趋势线安排专人跟进,将趋势线控制在合理的范围,从统计数据上解决面的问题而不是点对点的解决某一个问题。
运维管理
此前数据库的基础信息一直是存放Excel里面的所以很难想象这种Excel式运维有多痛苦!需要通过系统方式进行有效管理,只有这些基本元数据信息有效的管理起来后续功能才能依次展开,比如集群包含的实例信息,集群与集群组间的关系等,数据库与集群的关系,数据库内的对象信息等等。有了这些基本信息在对其打上各类运维需要的标签信息方便做更多细粒度的管理工作。
运维中很重要的一个工作就是发布尤其是在公司高速发展阶段产品功能快速迭代自然就涉及大量的发布工作,为了减轻DBA的负担让研发不在受制于DBA人力问题我们需要将他自助化。
研发可以通过系统自助完成发布工作,这里比较麻烦的是如何做好用户间的权限隔离避免误发布,这里需要结合公司SSO+系统元数据管理,其中系统元数据的有效性是整个系统能否做成功的最关键的因素,我们不能依靠DBA来维护元数据信息,必须依靠完善的系统机制保证做到元数据接近准实时状态,比如DB跟用户组织结构的关联问题,我们采用类似协商的机制来保障他们间的绝对准确,比如A先认领了数据库DB1,B用户也来认领该数据库如果他们同属一个组织结构则B的认领是合法的否则会被系统拒绝,后续如果A或者B组织结构发生调整则涉及该数据库的发布操作将被禁止,用户必须内部完成协商要么带走这个数据库(别人取消认领),要么留下这个数据库(自己取消认领)。类似的场景在系统里面有多处,如果依靠DBA来管理这些变化是非常困难的,因此元数据的自闭环管理是整个系统最底层的逻辑,元数据必须是%可靠的。很多系统比如CMDB之所以难做或者很少有公司CMDB能做好,个人理解很重要的一个原因就是在这里没做好。
发布系统实现上其实是有很多的复杂性的不仅仅是业务逻辑复杂,比如Sharding表跟普通表如何区分,Sharding表要怎么处理才能保证效率跟安全问题,大量DDL任务下发到执行服务器(DDL执行节点)如何保证执行节点不被打挂(我们基于gh-ost改造实现DDL,DDL过程会有非常大的Binlog解析动作,如果并发控制不好会导致执行节点网卡被打爆)等等这些都是比较复杂的问题。当然整个发布流程还涉及很多基础组件的设计开发工作,比如SQLReview/gh-ost/Rollback后续篇幅将依次介绍,发布系统使用简单但是开发过程确是一个系统性的工程有比较大的工作量,我们目前研发自助发布率几乎接近%,DBA不用在忙到半夜搞发布。
应急/自愈
数据库需要应急的场景最多的就是SQL把数据库打垮了,此前我们是通过操作阿里云的管理后台实现限流,但是这个方法只能使用于阿里云数据库,我们还有其他云数据库选型,而且操作阿里云限流效率不是特别的高,要登录到控制台,然后还要根据出问题的InstanceID找对应的实例,再找到管理页面操作......而且没有提供SDK操作不够便利,还记得我们前面一直提到的提供一致性的运维与研发体验吗?作为平台研发就要考虑通用的解决方案,比如通过我们自己的中间件来实现统一限流这样就不存在云上产品差异的问题,内部系统操作上也便利,也有sdk接口跟我们的DMS系统对接。
还有诸如冒烟事件处理自愈系统会实时检测系统存在的异常比如:未提交事务,锁等待,连接数飙升,长查询,SQL执行堆积,CPU飙升等系统会实时检测并针对性的启动相关处理规则将冒烟扑灭避免冒烟演变成火灾。
数据统计分析
通过平台的任务管理部署采集脚本每天对系统表: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数量就呈现指数级下降便于统计与分析。
DB上云与自建的差别
上述功能其实都不涉及传统自建时代围绕基础设施做的一系列的建设工作,功能本身更多是聚焦业务功能本身,因此开发过程中相对还是轻量的。如果自建则要考虑的问题非常多从硬件,系统,数据库,HA等等都是非常复杂的,过去DBA在这块积累的大量的经验每个资深DBA可能在这块都有专属于自己的黑科技,这也是DBA过去比较有价值跟难以替代的地方,但是随着DB的上云成为趋势传统的做法正在成为历史也逐渐的被新入行的DBA所淡忘,云对基础设施的封装是时代的进步。
回想10年前入行的时候那时还是11k/15k的SAS盘,还在折腾什么场景该用Raid10,还是Raid5,是WriteThrough还是WriteBack,不同机型配置下my.cnf该怎么设置,OS内核调参,不同数据库版本下特性有什么不同尤其复制的改进对实现HA起到什么影响,HA该怎么选做是MMM还是MHA还是ORC还是自己做一个HA系统,如何快速安装部署,快速搭建主从,备份是物理还是逻辑备份等等,这些随着技术的进步及云的进一步渗透这些正在成为远古记忆。
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左右的实例来说实在是太重了,接入中间件后连接数直接能从0降低到这还是非常可观的。这里顺便说一句之所以缓存既选择了Codis又有哨兵,还有Cluster,这里跟PHP都有一定关系,其中PHP业务就是Codis为主(所以前面吐槽php的原因就在这里,当然吐槽的不仅仅是数据库还有服务治理上的...),为了适应PHP的长期存在我们也在对Redis进行Mesh化建设与治理暂且按下不表。
同时的在数据库的深度可观测性上可以做更多工作,过去对热点SQL/SQL分布/RT分布是很难有合适的手段的(虽然后续有了PS功能但是还是显得很局限),通过中间件旁路这些信息可以轻松获取到。
有了中间件的加持+DBA服务治理+平台建设数据库的稳定性有了长效的保障机制。中间件的建设也为了解决一致性运维与研发体验提供一些支持。
MySQL基础工具建设
自愈系统:
系统会实时感知到数据库负载情况,结合数据库中间件的能力快速作出决策进行限流或者SQL查杀,同时基于云的弹性能力进行自动扩容云上都有类似的ModifyDBInstanceSpec接口,比如检测到空间不足则立即扩容,因此我们可以将实例空间维持在一个很高的使用水位尽量成本合理化。
SQLReview
目前在业内有很多开源的解决方案但是我还是坚持自己做了一个,理由也很简单可以自由灵活的个性化定制,不仅仅覆盖面要全面,同时也融入DBA经验性的内容,可以基于统计数据做公司内的“大数据”分析可以清楚的知道大家整体容易在什么地方犯错,哪些团队做的好哪些团队做的差等。同时自研一个因为完全能hold住跟其他自研系统可以无缝对接高度的灵活,此外还针对性的对货拉拉过去规范上的错误或者不足做出识别与纠正,如果基于开源就不太容易做到或者有一定改造成本。
SQLReview核心模块就是SQL解析这个参考了TIDB的实现模块有兴趣的可以看下其核心解析模块:githup.