货拉拉混合云数据库体系化建设
2021-11-28 21:27:11 阿炯
货拉拉作为一家业务遍及多个国家及地区的物流公司其面临的技术环境是非常复杂的,在云时代的浪潮里基于混合云快速构建环境搭建系统成了我们必然的选择。
题图来自“外部授权”
作者简介:
蔡鹏,前饿了么,蚂蚁金服技术专家,现任货拉拉数据库部门负责人,负责货拉拉混合云化业务场景下整体数据库,消息队列,缓存,数据库中间件的稳定性建设工作。
内容摘要:货拉拉作为一家业务遍及多个国家及地区的物流公司其面临的技术环境是非常复杂的,在云时代的浪潮里基于混合云快速构建环境搭建系统成了我们必然的选择。但是在混合云上如何对基于各家云构建的系统进行有效的管理是个挑战,尤其对处在系统最底层的数据库层面临的问题,业务诉求,各方痛点,是我及我的团队重点解决的。
混合云数据库治理背景介绍
从内容上我们可以看到我们面临的治理环境其实是非常复杂的,几乎可以用头大来形容。由于历史上的种种原因,数据库选型众多(主要原因是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有权利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及研发体验都非常好。
数据总线:
主要用于数据(监控metric类数据非业务数据)传输使用,比如从最远端的站点A到集中管控系统部署站点的网络延迟在600ms,如果A站点的监控数据要往管理站点上报要通过网络直接上报就显得很困难。
为了减少过多的Kafka部署增加运维的复杂性我们其他站点的监控数据统一上报到一个网络距离上处在相对中间的kafka集群,然后在通过mirror的方式将监控数据同步到管控节点进行集中的消费处理,整体看监控数据从最远端的站点传回到集中管控节点通常控制在1s内整体可以接受,其实最主要的是监控数据写到Kafka避免监控数据因为网络原因导致丢失。
平台组件集:
其他组件后续会陆续介绍,这里简单介绍一下任务调度
DBA日常工作中肯定会有很多定时任务要维护即使平台化了也仍旧需要,过去DBA是将这些任务通过crontab进行管理,但是这样是很局限的,比如获取执行状态、结果、日志比较麻烦,存在单点问题,脚本分散化不利于维护,年久容易失修一但被遗忘里面的脚本逻辑中可能会产生破坏作用,存在安全隐患,为此我们单独实现了一个调度管理系统对散落在各个站点上的任务进行统一集中管理。
调度本身实现是比较简单的可以理解成crontab的网络版,只是在调度本身的基础上添加了一些管理模块如:节点注册,通讯模块,心跳检测等,如果不清楚crontab实现原理的可以搜索Github有比较多的实现参考方式。其实功能还是比较简单的只是实现了调度的基本功能跟分布式部署,时间关系(70分标准)并未来得及将节点实现集群化避免调度单点问题比如某一个调度节点down机其他节点会自动接管。
添加一个任务:
注册一个脚本:
任务管理:
通过调度系统很快就完成收拢散落在各个站点上的crontab任务。
MySQL平台化建设
监控报警
健康大盘:
通过对数据库近50个监控指标的权重打分得到一个数据库实例的健康度,Dashboard直接按照健康度排序即可一眼看透当前所有实例的健康状况,同时在做权重打分时也会顺带将有问题的地方做好描述,方便DBA直接通过系统一眼定位问题。大致是这样的:
当然为了方便DBA能对问题进行快速深入的分析处理,我们几乎将DBA所有常用操作都封装到了监控大盘的快捷操作中。如:SQL快照(包含用户,状态,时间,锁信息等),实时抓取SQL信息,Kill SQL,SQL限流/熔断,报警屏蔽等。日常工作中尽量避免DBA通过命令行登录到数据库实例进行黑屏操作,这个是我们形成共识,不管多么资深的DBA一定在命令行模式下犯过一些低级错误!!因此平台化要能覆盖到这些微小的点。
报警:
我们数据库的报警量还是比较多的(每天2-500条左右),这也是我们做的不够好的地方,回顾我经历过的一些公司或者了解到的一些公司几乎都是有类似的问题,甚至每天收到上千条报警的也不在少数,报警看似是非常简单的但是要做好其实是非常困难的,既想报警少,又想核心报警不能漏,有时大家为了减少报警甚至是想干掉报警本身,而不是是去想着去解决问题,这是本末倒置的。
其实很多报警是可以不用发出来的,因为发出来也没有人去进一步处理或者被自愈系统自动处理掉了,但是种种原因考虑又不得不发,在治理水平跟不上时宁愿多报也不能漏报(漏报关键报警信息是非常严重的,如果有问题则罪加一等)。报警多其实反应的是治理水平没有跟上,有些问题看似是非致命性问题,但是如果长期不去处理问题最终会被放大甚至恶化成故障。我们的做法是将报警数据化统计分析呈现每日趋势重点盯住报警趋势线安排专人跟进,将趋势线控制在合理的范围,从统计数据上解决面的问题而不是点对点的解决某一个问题。
运维管理
此前数据库的基础信息一直是存放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不用在忙到半夜搞发布。
应急/自愈
数据库需要应急的场景最多的就是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年前入行的时候那时还是10k/15k的SAS盘,还在折腾什么场景该用Raid10,还是Raid5,是Write Through还是Write Back,不同机型配置下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左右的实例来说实在是太重了,接入中间件后连接数直接能从5000降低到300这还是非常可观的。这里顺便说一句之所以缓存既选择了Codis又有哨兵,还有Cluster,这里跟PHP都有一定关系,其中PHP业务就是Codis为主(所以前面吐槽php的原因就在这里,当然吐槽的不仅仅是数据库还有服务治理上的...),为了适应PHP的长期存在我们也在对Redis进行Mesh化建设与治理暂且按下不表。
同时的在数据库的深度可观测性上可以做更多工作,过去对热点SQL/SQL分布/RT分布是很难有合适的手段的(虽然后续有了PS功能但是还是显得很局限),通过中间件旁路这些信息可以轻松获取到。
有了中间件的加持+DBA服务治理+平台建设数据库的稳定性有了长效的保障机制。中间件的建设也为了解决一致性运维与研发体验提供一些支持。
MySQL基础工具建设
自愈系统:
系统会实时感知到数据库负载情况,结合数据库中间件的能力快速作出决策进行限流或者SQL查杀,同时基于云的弹性能力进行自动扩容云上都有类似的ModifyDBInstanceSpec接口,比如检测到空间不足则立即扩容,因此我们可以将实例空间维持在一个很高的使用水位尽量成本合理化。
SQLReview
目前在业内有很多开源的解决方案但是我还是坚持自己做了一个,理由也很简单可以自由灵活的个性化定制,不仅仅覆盖面要全面,同时也融入DBA经验性的内容,可以基于统计数据做公司内的“大数据”分析可以清楚的知道大家整体容易在什么地方犯错,哪些团队做的好哪些团队做的差等。同时自研一个因为完全能hold住跟其他自研系统可以无缝对接高度的灵活,此外还针对性的对货拉拉过去规范上的错误或者不足做出识别与纠正,如果基于开源就不太容易做到或者有一定改造成本。
SQLReview核心模块就是SQL解析这个参考了TIDB的实现模块有兴趣的可以看下其核心解析模块:githup.com/pingcap/tidb/ast,githup.com/pingcap/tidb/parser,不过这块随着源码的不断提交会有一定的差异,如果我们一直跟着官方最新代码包去编译容易产生非预期问题,建议是本地化包管理或者干脆把这个解析模块单独扣出来。
关于审核规则一方面充分吸收了开源系统的的一些规则还融入了一些DBA经验理解及过往错误规则纠正:
这些会在研发提交到发布系统前进行统一的校验。
gh-ost建设
gh-ost(相信很多人已经对他很熟悉了)只是一个单纯的DDL工具,如果跟平台进行整合还是要做一些改动的,以适应我们多站点化的部署:
根据统计我们平均每天的DDL量超过200+,如果是sharding一个逻辑表的DDL会被拆分成1024个DDL,如果这些DDL由一台机器来完成是非常困难的,而且研发在提交发布的时候可能都集中在发布窗口自动打开之后的一段时间内,因此即使在同一个站点内执行节点也需要部署多个,每次向执行节点分发任务都会根据执行节点的任务数来做均衡分发。之所以还需要在每个执行节点维护一个执行队列,主要是因为gh-ost本身导致的简单回顾一下其基本原理:
由于其通过拉取并解析Binlog来代替PT的触发器机制因此当对Binlog产生量比较大的实例进行DDL时网络带宽会比较高,当多个任务同时进行时带宽可能会被打满(在云上DMS使用的ECS,EC2网络配置都不是特别高基本都在4C~8C、2Gb~5Gb带宽),同时CPU也会很高主要是gh-ost要对记录做循环的逐行逐列处理,系统高负载下会导致DDL失败的概率增加。所以在每个执行节点上都加入了一个任务队列通过线程池做稳妥的并行化控制避免相互影响。
此外还通过网络模块对其进行良好的控制比如优雅终止,还有日志的阉割(其日志实在是有点啰嗦),由于比较简单不做赘述。
Flashback(DML回滚)
我们并没有将回滚内置到DMS发布系统里面主要还是嫌麻烦,毕竟实际需要回滚的场景还是非常稀少的,内置到发布系统做起来有点重当然好处是需要用的时候很快捷,我们是在gh-ost执行过程中埋点关键点位信息:start binlogfile~end binlogfile,start pos~end 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日常用到的绝大部分功能。功能实现上也没有特别多的复杂内容简单上图:
监控大盘
出于个人喜好的原因Redis沿袭了MySQL的大盘套路:
总的来说就要达到一眼定位问题,同时将所有DBA排查定位问题,日常操作等功能都集成进来比如:
实例替换
可以快速完成实例的迁移替换及某一台机器的快速替换。主要用在服务器内存超卖策略下某一个机器内存不足或者单个实例过大时的便捷操作。由于我们整个db/缓存/队列的资源交付统一是资源id的交付模式原则上底层资源变动对上游业务无感:
研发不用关注数据源的配置是什么,只要将资源id跟appid建立绑定关系即可访问到数据。
扩缩容
扩缩缩容的背后系统维护了一个资源池我们会常态化的保持池内有一定的闲置资源确保随时可以使用。
全量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后也出现了很多其他的一些问题:
1. 连接压力会比较大主要是一些高频的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',
....
];
2. java客户端不统一对pipline支持有差异,同时也在Lettuce重连机制上多次采坑,云上ECS都是跨AZ的云上有时会遇到网络抖动情况,一但出现网络问题Lettuce就会有问题详见:https://www.debugger.wiki/article/html/1615624562913635 为此我们框架层对Lettuce重连机制做了改动才最终解决这个问题。
3. cluster这种多节点的集群模式会多分配很多内存资源跟服务器资源,同时我们经过统计50%的实例使用率都低于1GB,集群内存使用率非常低(我们最小规格为3M3S 3GB内存资源)。再加上slave是有很大程度的资源浪费的
总的来说在redis的基础运维上全面实现cluster化后我们当前的方式是没有太大问题的,但是在进一步的深入治理上我们还是做的不够的,因此我们也正在做我们的mesh化建设。
Redis ServiceMesh建设
所谓mesh化其实就是将proxy本地化内置到客户端服务器即sidecar模式。local proxy可以充分利用客户端资源,缩短链路减少云上跨AZ带来网络上的不确定性,我们前面在codis上也出现过客户端跨多AZ跟proyx间网络问题导致部分client有影响。
服务&成本治理
a. 多语言统一访问层:这里跟前面讲的数据库中间件性质类似不做赘述
b. 多租户:前面介绍过cluster模式下的天然资源浪费,单纯依靠运维手段是难以很好的解决的,我们为了解决这个问题我们引入了多租户的概念,即一个cluster集群在proxy层进加一个逻辑划分既:namespace,同时基于user/pasword的认证,用户在读取写入时会给每一个key强制加上一个key前缀来避免key冲突:
但是这也还是有缺陷的,虽然集群内部key是可以避免冲突的但是没办法做到资源的隔离,只是逻辑的隔离,我们将一些小容量的cluster集中迁移到统一的大集群中进行租户划分,尽管无法做到资源隔离但是通过适度的资源冗余也能很好的避免性能问题,同时由于混部集中会有效减少资源浪费,根据我们现有的数据测算可以节约40%~60的内存成本,同时有效减少现有集群规模跟节点数量。
c. 在可观测性上,通过proxy对key的旁路与分析可以实时感知到大key(虽然通过Agent可以每日分析但是这个实时性比较差会漏掉已过期的key)、热key、及每个命令类型的rt分布情况。
d. 辅助治理:比如可以实时检测到非法命令的使用及拦截、同时每个key都有特定的用户标签遇到大key、热key比较容易定位所属研发
e. 研发友好:统一的pipline,通过proxy本地化热key缓存等。
f. 副作用:key长度增加
Kafka平台化建设
Kafka治理背景
1. 一种开源工具拼凑的管理系统
○ 部署在各个IDC,管理分散,功能弱,不成体系
2. 管控能力弱,碎片化监控
○ 消费延迟监控不直观,单纯Offset Lag没有意义
○ Broker监控要额外部署相关Export及监控报警系统
○ Topic相互之间资源竞争,多次出现一些高流量的topic将整个系统资源打满影响其他topic的正常使用。
3. 集群内部对象状态缺乏感知
○ consumer/topic/partition等核心对象无收集统计,无法主动感知业务异常
○ 核心对象缺乏基础监控数据无法做沉淀分析,运维靠人肉经验指导
○ 问题定位缺乏必要的数据支持,服务稳定性完全依靠人工死磕
4. 平台化建设缺乏有效参考
○ 业内开源系统稀少,没有可以直接使用的
○ 可观测性不够友好或者目前只对java会比较好
Kafka平台
集群管理
此前我们都是在各个站点进行分别部署对应的开源管理系统,管理比较分散,现在得益于现有架构的实现我们可以轻松实现一站式管控。
Topic管理
除了对topic的常规操作封装到平台外,还通过对topic元数据信息落库我们可以在全局的角度分析一些问题比如当前那些topic是热点topic,那些topic msg body比较大(可能需要DBA找下业务方确认合理性,很多数据来源是canal->mysql->kafka研发为了简单可能会将全字段写入kafka),那些topic体量比较大等等。
Topic详细信息:
通过对每个topic的消息大小采样,可以知道消息体大小分布情况及通过采样消息分析消息体大小突变的原因,方便作出优化处理。
Topic分区信息:
同时对topic的分区分布情况进行展示(实现对kafka对象信息的完整覆盖,完全实现了kafka-manager的全部功能,篇幅原因不做详细介绍)
同时为了方便研发&DBA临时查看消息的诉求也提供基础的查询能力。
Consumer管理
系统将topic跟consumer的关系进行采集保留方便运维需要。
Consumer Lag:
一般的kafka延迟通常以lags来衡量,但是lags最大的问题在于难以做时间维度的量化,以至于难以判断延迟的真正程度,也不方便提供研发订阅(研发需要知道自己消费延迟情况但是lags对研发不够直观友好),比如上图lag=250k表达出来的意思就不如延迟时间=600s来的直观,基于时间的延迟实现我们在提供研发做报警订阅时就有了直观的衡量标准。
但是受到kafka HW及consumer延迟commit的影响要获取consumer的精确延迟时间是不可能的,以上图为例这是典型的大数据消费处理的特征:消费一批数据后在做commit,这时我们观察到他的延迟时间就跟commit时机有非常大关系,呈现出了周期性波动,但是实际上延迟的真实时间是小于600s的,但是受到上述机制的影响我们没有办法做精确计算。
如上图,我们可推测该consumer消费时就是典型的生产业务消费特征:逐条或者小批量消费后立即commit。我们看到他的延迟就相对稳定(生产与消费比较稳定无明显的周期波动)波动在正常范畴内。
计算延迟时间思路:
1.基于msg body时间戳法:
delay~= (hw offset msg body).timestamp-(consumer offset msg body).timestamp
通过获取两个offset点位的消息体中的时间戳做减法的方式效率比较低理论上准确度好一点。
2.max(分区延迟lags/平均分区生产速度)
非常高效但是准确度稍差,目前主要采用这种方式。
说明:实际实现要稍微复杂一点点,两种方案都会遇到很多细节要处理,篇幅原因不在细节上面面俱到,再次申明这是不精确的,只是相比lags要更加可度量,方便做报警配置及研发订阅告警。
Broker管理
我们对broker做了zone分组方便实现后续的资源隔离。
元数据网关
目前研发消费topic时只需要拿到topic所在集群地址即可消费,这样就给我们管理带来一定问题同时也不够安全。
前面提到,我们现在所有的资源交付都是通过资源id进行交付的,研发也必须只能通过资源id才能访问到Kafka,因此我们网关就基于这个点进行建设:用户的consumer必须在DBA交付的资源ID中建立topic跟consumer的绑定关系,用户在拿资源id时需要对其指定的password/topic跟consumer进行绑定关系验证(框架对kafka client做了封装)否则就拿不到资源id,当然这个实现肯定是不完美的,聪明的研发肯定能想到办法绕过,比较理想的做法就是将绑定关系内置到kafka server中,但是目前由于还没有精力进行这方面的投入。
多租户隔离
我们目前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的调用实现,一种非常取巧的实现方式。同时元数据的采集也对我们更好的维护治理这些基础中间件提供更好的依据。
ES
对集群有了更多运维属性的标签便于维护
通过DMS对索引添加研发属性描述,便于后续维护
资源申请与统一交付
索引轮转统一管理,减轻研发心智
MQ
一个完整版的MQ-Admin,实现管理从分散到集中化的管理,操作便捷高效。
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的思考
云极大的简化传统自建时代围绕基础设施稳定性建设的复杂性,云已经成为了一种新的基础设施。
DBA职责转变:
云上数据库的可靠性稳定性在云能力的加持下不再是核心工作,DBA将更加偏向业务比如性能优化,成本优化等等。
我们在自建时代也会考虑成本但是通常都非常难做到非常高的资源利用率,这里面有非常多的原因,近几年也看到将db搬到k8s的做法其实本质的就是想通过这样的手段来达到类似云的能力,但是这个投入是非常大的甚至超过了收益,也伴随很多的风险,非常不建议中小公司做这种尝试,个人也非常不看好k8s里面跑db的做法(云原生才是未来),千万不要以为能将它跑起来就是能驾驭的了他。
能力模型改变:
围绕自建打造的传统能力将没有生命力(还有比一个SDK来的更简单高效的吗),DBA在自建时代的很多“黑科技”将逐步失去用武之地。过去DBA深入研究数据库内核比如对MySQL的各种原理机制理解的非常透彻,虽然这些也还是非常重要但是留给DBA的操作空间并不大,比如云上有规格跟参数模板严格的参数控制,基于开源理解的特性跟云上优化或者改造后的并不完全一致,因此DBA深入理解云产品比深如理解数据库本身可能更有帮助(不过遗憾的是目前云产品本身的帮助文档还非常的不健全或者介绍的过于简单了)。
上云误区:云上DBA仍旧有用武之地,仍旧有很多有挑战的事情要去解决。
转型思考:
拥抱变化上云不可抵抗,不要把自己定位是一个DBA这太局限了(省略后续鸡汤)。
实际使用云的过程中从各家云提供的SDK已经高度模块化了,稍微有点编程能力就可以将这些SDK组装起来平凑成你要的样子,很大程度上降低了开发的复杂性。
不知道我们是否思考过一个问题:为什么每家工作不管是DBA还是其他运维部门都还在不遗余力的做平台?这些平台其实功能都是同质化的,或者说高度的重复性建设,虽然呈现形态不一样但是内部的机制几乎都一样。我觉得未来这种局面会随着云上对运维标准的定义跟建立而发生很大改变,或许以后我们不用在建立平台直接使用云平台,或者采购三方开发者提供的标准化解决方案。
云正在重新塑造整个IT行业,作为从业者积极调整改变很重要,如果打不过他们就加入他们(云厂商)。
上云之后 DBA 会原地失业吗,其实多数情况都不会,那上云后还有哪些事需要 DBA 去做的呢,这节内容就来盘点一些。
1)恢复演练
对于 MySQL 实例、单库、单表,定期进行恢复演练,现在很多云厂商的数据库服务虽然自带这些功能,但是根据以往的经验,第一次恢复多多少少还是会遇到一些问题的。如果有开发能力,后续可以写成程序,通过程序调用云厂商接口的形式实现。
2)资源缩减
很多业务上线之后,往往达不到当时预估的峰值,我们可以做的就是对资源使用率进行评估,然后进行资源缩减,从而节约成本。
3)答疑
跟以前一样,DBA 工作的一部分就是答疑。比如:数据库默认存储引擎、隔离级别,实例对应的 QPS,当然这类重复性的问题可以整理成文档。
4)增加监控/告警处理
云厂商自带的监控不能适用于所有场景,所以我们总要增加一些特有的监控项,并及时处理告警。
5)Redis 大 key 分析
很多云厂商自带 Redis 大 key 分析结果,如果我们要按业务集中展示出来,可以通过调用云厂商的接口,获取大 key 分析结果,然后基于业务分组,并展示给研发。
6)数据库巡检
比如巡检 RDS、Redis 的付费方式,剩余时间,时区等。有异常发送给 DBA,防止出现线上数据库服务到期未续费或者时区不统一的低级错误。
7)评分系统
对于每个 RDS 或者 Redis 或者 MongoDB,形成一个评分,具体打分维度比如:Slow Log 条数、CPU 最大使用率、大表数量、表数量、磁盘使用率、连接数使用率、IOPS 使用率等。
8)迁移支持
有时需要从机房迁移到云上,或者在不同云厂商之间迁移。需要找到合适的数据迁移方案(很多云厂商都有 DTS,一般支持很多场景的数据迁移),并且参数要统一(比如时区、SQL Mode 等)。
9)资源申请/扩缩容
经常会遇到一些资源申请或者扩缩容的需求。可以在页面点,可以调云厂商的接口,也可以借助其他工具(比如 Terraform)。
10)分库分表支持
通常情况也是能不分就不分,如果业务实在有分库分表的需求,需要 DBA 配合他们做。
11)SQL 审核
很多云厂商都有自己的 SQL 审核产品,比如阿里云的 DMS,如果公司采购了,需要 DBA 熟悉他的使用,以方便日常的审核或者答疑。当然 DMS 不便宜,如果有条件,可以自行开发一个 SQL 审核工具。
12)SQL 优化
SQL 优化也是要继续做的事情,尽管有些云厂商自带慢查询优化建议,但是也要 DBA 去验证优化建议是否可行,并且需要跟进研发后续是否优化了。
13)架构设计
可以给业务一些架构设计上的建议(比如某项功能使用哪种数据库最好,当然前提是我们要学习更多的数据库)。
14)开发规范
可以结合公司场景制定一个数据库开发规范,定好规范并真正实施之后,其实很多问题都可以避免的。
题图来自“外部授权”
作者简介:
蔡鹏,前饿了么,蚂蚁金服技术专家,现任货拉拉数据库部门负责人,负责货拉拉混合云化业务场景下整体数据库,消息队列,缓存,数据库中间件的稳定性建设工作。
内容摘要:货拉拉作为一家业务遍及多个国家及地区的物流公司其面临的技术环境是非常复杂的,在云时代的浪潮里基于混合云快速构建环境搭建系统成了我们必然的选择。但是在混合云上如何对基于各家云构建的系统进行有效的管理是个挑战,尤其对处在系统最底层的数据库层面临的问题,业务诉求,各方痛点,是我及我的团队重点解决的。
混合云数据库治理背景介绍
从内容上我们可以看到我们面临的治理环境其实是非常复杂的,几乎可以用头大来形容。由于历史上的种种原因,数据库选型众多(主要原因是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有权利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及研发体验都非常好。
数据总线:
主要用于数据(监控metric类数据非业务数据)传输使用,比如从最远端的站点A到集中管控系统部署站点的网络延迟在600ms,如果A站点的监控数据要往管理站点上报要通过网络直接上报就显得很困难。
为了减少过多的Kafka部署增加运维的复杂性我们其他站点的监控数据统一上报到一个网络距离上处在相对中间的kafka集群,然后在通过mirror的方式将监控数据同步到管控节点进行集中的消费处理,整体看监控数据从最远端的站点传回到集中管控节点通常控制在1s内整体可以接受,其实最主要的是监控数据写到Kafka避免监控数据因为网络原因导致丢失。
平台组件集:
其他组件后续会陆续介绍,这里简单介绍一下任务调度
DBA日常工作中肯定会有很多定时任务要维护即使平台化了也仍旧需要,过去DBA是将这些任务通过crontab进行管理,但是这样是很局限的,比如获取执行状态、结果、日志比较麻烦,存在单点问题,脚本分散化不利于维护,年久容易失修一但被遗忘里面的脚本逻辑中可能会产生破坏作用,存在安全隐患,为此我们单独实现了一个调度管理系统对散落在各个站点上的任务进行统一集中管理。
调度本身实现是比较简单的可以理解成crontab的网络版,只是在调度本身的基础上添加了一些管理模块如:节点注册,通讯模块,心跳检测等,如果不清楚crontab实现原理的可以搜索Github有比较多的实现参考方式。其实功能还是比较简单的只是实现了调度的基本功能跟分布式部署,时间关系(70分标准)并未来得及将节点实现集群化避免调度单点问题比如某一个调度节点down机其他节点会自动接管。
添加一个任务:
注册一个脚本:
任务管理:
通过调度系统很快就完成收拢散落在各个站点上的crontab任务。
MySQL平台化建设
监控报警
健康大盘:
通过对数据库近50个监控指标的权重打分得到一个数据库实例的健康度,Dashboard直接按照健康度排序即可一眼看透当前所有实例的健康状况,同时在做权重打分时也会顺带将有问题的地方做好描述,方便DBA直接通过系统一眼定位问题。大致是这样的:
当然为了方便DBA能对问题进行快速深入的分析处理,我们几乎将DBA所有常用操作都封装到了监控大盘的快捷操作中。如:SQL快照(包含用户,状态,时间,锁信息等),实时抓取SQL信息,Kill SQL,SQL限流/熔断,报警屏蔽等。日常工作中尽量避免DBA通过命令行登录到数据库实例进行黑屏操作,这个是我们形成共识,不管多么资深的DBA一定在命令行模式下犯过一些低级错误!!因此平台化要能覆盖到这些微小的点。
报警:
我们数据库的报警量还是比较多的(每天2-500条左右),这也是我们做的不够好的地方,回顾我经历过的一些公司或者了解到的一些公司几乎都是有类似的问题,甚至每天收到上千条报警的也不在少数,报警看似是非常简单的但是要做好其实是非常困难的,既想报警少,又想核心报警不能漏,有时大家为了减少报警甚至是想干掉报警本身,而不是是去想着去解决问题,这是本末倒置的。
其实很多报警是可以不用发出来的,因为发出来也没有人去进一步处理或者被自愈系统自动处理掉了,但是种种原因考虑又不得不发,在治理水平跟不上时宁愿多报也不能漏报(漏报关键报警信息是非常严重的,如果有问题则罪加一等)。报警多其实反应的是治理水平没有跟上,有些问题看似是非致命性问题,但是如果长期不去处理问题最终会被放大甚至恶化成故障。我们的做法是将报警数据化统计分析呈现每日趋势重点盯住报警趋势线安排专人跟进,将趋势线控制在合理的范围,从统计数据上解决面的问题而不是点对点的解决某一个问题。
运维管理
此前数据库的基础信息一直是存放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不用在忙到半夜搞发布。
应急/自愈
数据库需要应急的场景最多的就是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年前入行的时候那时还是10k/15k的SAS盘,还在折腾什么场景该用Raid10,还是Raid5,是Write Through还是Write Back,不同机型配置下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左右的实例来说实在是太重了,接入中间件后连接数直接能从5000降低到300这还是非常可观的。这里顺便说一句之所以缓存既选择了Codis又有哨兵,还有Cluster,这里跟PHP都有一定关系,其中PHP业务就是Codis为主(所以前面吐槽php的原因就在这里,当然吐槽的不仅仅是数据库还有服务治理上的...),为了适应PHP的长期存在我们也在对Redis进行Mesh化建设与治理暂且按下不表。
同时的在数据库的深度可观测性上可以做更多工作,过去对热点SQL/SQL分布/RT分布是很难有合适的手段的(虽然后续有了PS功能但是还是显得很局限),通过中间件旁路这些信息可以轻松获取到。
有了中间件的加持+DBA服务治理+平台建设数据库的稳定性有了长效的保障机制。中间件的建设也为了解决一致性运维与研发体验提供一些支持。
MySQL基础工具建设
自愈系统:
系统会实时感知到数据库负载情况,结合数据库中间件的能力快速作出决策进行限流或者SQL查杀,同时基于云的弹性能力进行自动扩容云上都有类似的ModifyDBInstanceSpec接口,比如检测到空间不足则立即扩容,因此我们可以将实例空间维持在一个很高的使用水位尽量成本合理化。
SQLReview
目前在业内有很多开源的解决方案但是我还是坚持自己做了一个,理由也很简单可以自由灵活的个性化定制,不仅仅覆盖面要全面,同时也融入DBA经验性的内容,可以基于统计数据做公司内的“大数据”分析可以清楚的知道大家整体容易在什么地方犯错,哪些团队做的好哪些团队做的差等。同时自研一个因为完全能hold住跟其他自研系统可以无缝对接高度的灵活,此外还针对性的对货拉拉过去规范上的错误或者不足做出识别与纠正,如果基于开源就不太容易做到或者有一定改造成本。
SQLReview核心模块就是SQL解析这个参考了TIDB的实现模块有兴趣的可以看下其核心解析模块:githup.com/pingcap/tidb/ast,githup.com/pingcap/tidb/parser,不过这块随着源码的不断提交会有一定的差异,如果我们一直跟着官方最新代码包去编译容易产生非预期问题,建议是本地化包管理或者干脆把这个解析模块单独扣出来。
关于审核规则一方面充分吸收了开源系统的的一些规则还融入了一些DBA经验理解及过往错误规则纠正:
这些会在研发提交到发布系统前进行统一的校验。
gh-ost建设
gh-ost(相信很多人已经对他很熟悉了)只是一个单纯的DDL工具,如果跟平台进行整合还是要做一些改动的,以适应我们多站点化的部署:
根据统计我们平均每天的DDL量超过200+,如果是sharding一个逻辑表的DDL会被拆分成1024个DDL,如果这些DDL由一台机器来完成是非常困难的,而且研发在提交发布的时候可能都集中在发布窗口自动打开之后的一段时间内,因此即使在同一个站点内执行节点也需要部署多个,每次向执行节点分发任务都会根据执行节点的任务数来做均衡分发。之所以还需要在每个执行节点维护一个执行队列,主要是因为gh-ost本身导致的简单回顾一下其基本原理:
由于其通过拉取并解析Binlog来代替PT的触发器机制因此当对Binlog产生量比较大的实例进行DDL时网络带宽会比较高,当多个任务同时进行时带宽可能会被打满(在云上DMS使用的ECS,EC2网络配置都不是特别高基本都在4C~8C、2Gb~5Gb带宽),同时CPU也会很高主要是gh-ost要对记录做循环的逐行逐列处理,系统高负载下会导致DDL失败的概率增加。所以在每个执行节点上都加入了一个任务队列通过线程池做稳妥的并行化控制避免相互影响。
此外还通过网络模块对其进行良好的控制比如优雅终止,还有日志的阉割(其日志实在是有点啰嗦),由于比较简单不做赘述。
Flashback(DML回滚)
我们并没有将回滚内置到DMS发布系统里面主要还是嫌麻烦,毕竟实际需要回滚的场景还是非常稀少的,内置到发布系统做起来有点重当然好处是需要用的时候很快捷,我们是在gh-ost执行过程中埋点关键点位信息:start binlogfile~end binlogfile,start pos~end 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日常用到的绝大部分功能。功能实现上也没有特别多的复杂内容简单上图:
监控大盘
出于个人喜好的原因Redis沿袭了MySQL的大盘套路:
总的来说就要达到一眼定位问题,同时将所有DBA排查定位问题,日常操作等功能都集成进来比如:
实例替换
可以快速完成实例的迁移替换及某一台机器的快速替换。主要用在服务器内存超卖策略下某一个机器内存不足或者单个实例过大时的便捷操作。由于我们整个db/缓存/队列的资源交付统一是资源id的交付模式原则上底层资源变动对上游业务无感:
研发不用关注数据源的配置是什么,只要将资源id跟appid建立绑定关系即可访问到数据。
扩缩容
扩缩缩容的背后系统维护了一个资源池我们会常态化的保持池内有一定的闲置资源确保随时可以使用。
全量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后也出现了很多其他的一些问题:
1. 连接压力会比较大主要是一些高频的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',
....
];
2. java客户端不统一对pipline支持有差异,同时也在Lettuce重连机制上多次采坑,云上ECS都是跨AZ的云上有时会遇到网络抖动情况,一但出现网络问题Lettuce就会有问题详见:https://www.debugger.wiki/article/html/1615624562913635 为此我们框架层对Lettuce重连机制做了改动才最终解决这个问题。
3. cluster这种多节点的集群模式会多分配很多内存资源跟服务器资源,同时我们经过统计50%的实例使用率都低于1GB,集群内存使用率非常低(我们最小规格为3M3S 3GB内存资源)。再加上slave是有很大程度的资源浪费的
总的来说在redis的基础运维上全面实现cluster化后我们当前的方式是没有太大问题的,但是在进一步的深入治理上我们还是做的不够的,因此我们也正在做我们的mesh化建设。
Redis ServiceMesh建设
所谓mesh化其实就是将proxy本地化内置到客户端服务器即sidecar模式。local proxy可以充分利用客户端资源,缩短链路减少云上跨AZ带来网络上的不确定性,我们前面在codis上也出现过客户端跨多AZ跟proyx间网络问题导致部分client有影响。
服务&成本治理
a. 多语言统一访问层:这里跟前面讲的数据库中间件性质类似不做赘述
b. 多租户:前面介绍过cluster模式下的天然资源浪费,单纯依靠运维手段是难以很好的解决的,我们为了解决这个问题我们引入了多租户的概念,即一个cluster集群在proxy层进加一个逻辑划分既:namespace,同时基于user/pasword的认证,用户在读取写入时会给每一个key强制加上一个key前缀来避免key冲突:
但是这也还是有缺陷的,虽然集群内部key是可以避免冲突的但是没办法做到资源的隔离,只是逻辑的隔离,我们将一些小容量的cluster集中迁移到统一的大集群中进行租户划分,尽管无法做到资源隔离但是通过适度的资源冗余也能很好的避免性能问题,同时由于混部集中会有效减少资源浪费,根据我们现有的数据测算可以节约40%~60的内存成本,同时有效减少现有集群规模跟节点数量。
c. 在可观测性上,通过proxy对key的旁路与分析可以实时感知到大key(虽然通过Agent可以每日分析但是这个实时性比较差会漏掉已过期的key)、热key、及每个命令类型的rt分布情况。
d. 辅助治理:比如可以实时检测到非法命令的使用及拦截、同时每个key都有特定的用户标签遇到大key、热key比较容易定位所属研发
e. 研发友好:统一的pipline,通过proxy本地化热key缓存等。
f. 副作用:key长度增加
Kafka平台化建设
Kafka治理背景
1. 一种开源工具拼凑的管理系统
○ 部署在各个IDC,管理分散,功能弱,不成体系
2. 管控能力弱,碎片化监控
○ 消费延迟监控不直观,单纯Offset Lag没有意义
○ Broker监控要额外部署相关Export及监控报警系统
○ Topic相互之间资源竞争,多次出现一些高流量的topic将整个系统资源打满影响其他topic的正常使用。
3. 集群内部对象状态缺乏感知
○ consumer/topic/partition等核心对象无收集统计,无法主动感知业务异常
○ 核心对象缺乏基础监控数据无法做沉淀分析,运维靠人肉经验指导
○ 问题定位缺乏必要的数据支持,服务稳定性完全依靠人工死磕
4. 平台化建设缺乏有效参考
○ 业内开源系统稀少,没有可以直接使用的
○ 可观测性不够友好或者目前只对java会比较好
Kafka平台
集群管理
此前我们都是在各个站点进行分别部署对应的开源管理系统,管理比较分散,现在得益于现有架构的实现我们可以轻松实现一站式管控。
Topic管理
除了对topic的常规操作封装到平台外,还通过对topic元数据信息落库我们可以在全局的角度分析一些问题比如当前那些topic是热点topic,那些topic msg body比较大(可能需要DBA找下业务方确认合理性,很多数据来源是canal->mysql->kafka研发为了简单可能会将全字段写入kafka),那些topic体量比较大等等。
Topic详细信息:
通过对每个topic的消息大小采样,可以知道消息体大小分布情况及通过采样消息分析消息体大小突变的原因,方便作出优化处理。
Topic分区信息:
同时对topic的分区分布情况进行展示(实现对kafka对象信息的完整覆盖,完全实现了kafka-manager的全部功能,篇幅原因不做详细介绍)
同时为了方便研发&DBA临时查看消息的诉求也提供基础的查询能力。
Consumer管理
系统将topic跟consumer的关系进行采集保留方便运维需要。
Consumer Lag:
一般的kafka延迟通常以lags来衡量,但是lags最大的问题在于难以做时间维度的量化,以至于难以判断延迟的真正程度,也不方便提供研发订阅(研发需要知道自己消费延迟情况但是lags对研发不够直观友好),比如上图lag=250k表达出来的意思就不如延迟时间=600s来的直观,基于时间的延迟实现我们在提供研发做报警订阅时就有了直观的衡量标准。
但是受到kafka HW及consumer延迟commit的影响要获取consumer的精确延迟时间是不可能的,以上图为例这是典型的大数据消费处理的特征:消费一批数据后在做commit,这时我们观察到他的延迟时间就跟commit时机有非常大关系,呈现出了周期性波动,但是实际上延迟的真实时间是小于600s的,但是受到上述机制的影响我们没有办法做精确计算。
如上图,我们可推测该consumer消费时就是典型的生产业务消费特征:逐条或者小批量消费后立即commit。我们看到他的延迟就相对稳定(生产与消费比较稳定无明显的周期波动)波动在正常范畴内。
计算延迟时间思路:
1.基于msg body时间戳法:
delay~= (hw offset msg body).timestamp-(consumer offset msg body).timestamp
通过获取两个offset点位的消息体中的时间戳做减法的方式效率比较低理论上准确度好一点。
2.max(分区延迟lags/平均分区生产速度)
非常高效但是准确度稍差,目前主要采用这种方式。
说明:实际实现要稍微复杂一点点,两种方案都会遇到很多细节要处理,篇幅原因不在细节上面面俱到,再次申明这是不精确的,只是相比lags要更加可度量,方便做报警配置及研发订阅告警。
Broker管理
我们对broker做了zone分组方便实现后续的资源隔离。
元数据网关
目前研发消费topic时只需要拿到topic所在集群地址即可消费,这样就给我们管理带来一定问题同时也不够安全。
前面提到,我们现在所有的资源交付都是通过资源id进行交付的,研发也必须只能通过资源id才能访问到Kafka,因此我们网关就基于这个点进行建设:用户的consumer必须在DBA交付的资源ID中建立topic跟consumer的绑定关系,用户在拿资源id时需要对其指定的password/topic跟consumer进行绑定关系验证(框架对kafka client做了封装)否则就拿不到资源id,当然这个实现肯定是不完美的,聪明的研发肯定能想到办法绕过,比较理想的做法就是将绑定关系内置到kafka server中,但是目前由于还没有精力进行这方面的投入。
多租户隔离
我们目前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的调用实现,一种非常取巧的实现方式。同时元数据的采集也对我们更好的维护治理这些基础中间件提供更好的依据。
ES
对集群有了更多运维属性的标签便于维护
通过DMS对索引添加研发属性描述,便于后续维护
资源申请与统一交付
索引轮转统一管理,减轻研发心智
MQ
一个完整版的MQ-Admin,实现管理从分散到集中化的管理,操作便捷高效。
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的思考
云极大的简化传统自建时代围绕基础设施稳定性建设的复杂性,云已经成为了一种新的基础设施。
DBA职责转变:
云上数据库的可靠性稳定性在云能力的加持下不再是核心工作,DBA将更加偏向业务比如性能优化,成本优化等等。
我们在自建时代也会考虑成本但是通常都非常难做到非常高的资源利用率,这里面有非常多的原因,近几年也看到将db搬到k8s的做法其实本质的就是想通过这样的手段来达到类似云的能力,但是这个投入是非常大的甚至超过了收益,也伴随很多的风险,非常不建议中小公司做这种尝试,个人也非常不看好k8s里面跑db的做法(云原生才是未来),千万不要以为能将它跑起来就是能驾驭的了他。
能力模型改变:
围绕自建打造的传统能力将没有生命力(还有比一个SDK来的更简单高效的吗),DBA在自建时代的很多“黑科技”将逐步失去用武之地。过去DBA深入研究数据库内核比如对MySQL的各种原理机制理解的非常透彻,虽然这些也还是非常重要但是留给DBA的操作空间并不大,比如云上有规格跟参数模板严格的参数控制,基于开源理解的特性跟云上优化或者改造后的并不完全一致,因此DBA深入理解云产品比深如理解数据库本身可能更有帮助(不过遗憾的是目前云产品本身的帮助文档还非常的不健全或者介绍的过于简单了)。
上云误区:云上DBA仍旧有用武之地,仍旧有很多有挑战的事情要去解决。
转型思考:
拥抱变化上云不可抵抗,不要把自己定位是一个DBA这太局限了(省略后续鸡汤)。
实际使用云的过程中从各家云提供的SDK已经高度模块化了,稍微有点编程能力就可以将这些SDK组装起来平凑成你要的样子,很大程度上降低了开发的复杂性。
不知道我们是否思考过一个问题:为什么每家工作不管是DBA还是其他运维部门都还在不遗余力的做平台?这些平台其实功能都是同质化的,或者说高度的重复性建设,虽然呈现形态不一样但是内部的机制几乎都一样。我觉得未来这种局面会随着云上对运维标准的定义跟建立而发生很大改变,或许以后我们不用在建立平台直接使用云平台,或者采购三方开发者提供的标准化解决方案。
云正在重新塑造整个IT行业,作为从业者积极调整改变很重要,如果打不过他们就加入他们(云厂商)。
上云之后 DBA 会原地失业吗,其实多数情况都不会,那上云后还有哪些事需要 DBA 去做的呢,这节内容就来盘点一些。
1)恢复演练
对于 MySQL 实例、单库、单表,定期进行恢复演练,现在很多云厂商的数据库服务虽然自带这些功能,但是根据以往的经验,第一次恢复多多少少还是会遇到一些问题的。如果有开发能力,后续可以写成程序,通过程序调用云厂商接口的形式实现。
2)资源缩减
很多业务上线之后,往往达不到当时预估的峰值,我们可以做的就是对资源使用率进行评估,然后进行资源缩减,从而节约成本。
3)答疑
跟以前一样,DBA 工作的一部分就是答疑。比如:数据库默认存储引擎、隔离级别,实例对应的 QPS,当然这类重复性的问题可以整理成文档。
4)增加监控/告警处理
云厂商自带的监控不能适用于所有场景,所以我们总要增加一些特有的监控项,并及时处理告警。
5)Redis 大 key 分析
很多云厂商自带 Redis 大 key 分析结果,如果我们要按业务集中展示出来,可以通过调用云厂商的接口,获取大 key 分析结果,然后基于业务分组,并展示给研发。
6)数据库巡检
比如巡检 RDS、Redis 的付费方式,剩余时间,时区等。有异常发送给 DBA,防止出现线上数据库服务到期未续费或者时区不统一的低级错误。
7)评分系统
对于每个 RDS 或者 Redis 或者 MongoDB,形成一个评分,具体打分维度比如:Slow Log 条数、CPU 最大使用率、大表数量、表数量、磁盘使用率、连接数使用率、IOPS 使用率等。
8)迁移支持
有时需要从机房迁移到云上,或者在不同云厂商之间迁移。需要找到合适的数据迁移方案(很多云厂商都有 DTS,一般支持很多场景的数据迁移),并且参数要统一(比如时区、SQL Mode 等)。
9)资源申请/扩缩容
经常会遇到一些资源申请或者扩缩容的需求。可以在页面点,可以调云厂商的接口,也可以借助其他工具(比如 Terraform)。
10)分库分表支持
通常情况也是能不分就不分,如果业务实在有分库分表的需求,需要 DBA 配合他们做。
11)SQL 审核
很多云厂商都有自己的 SQL 审核产品,比如阿里云的 DMS,如果公司采购了,需要 DBA 熟悉他的使用,以方便日常的审核或者答疑。当然 DMS 不便宜,如果有条件,可以自行开发一个 SQL 审核工具。
12)SQL 优化
SQL 优化也是要继续做的事情,尽管有些云厂商自带慢查询优化建议,但是也要 DBA 去验证优化建议是否可行,并且需要跟进研发后续是否优化了。
13)架构设计
可以给业务一些架构设计上的建议(比如某项功能使用哪种数据库最好,当然前提是我们要学习更多的数据库)。
14)开发规范
可以结合公司场景制定一个数据库开发规范,定好规范并真正实施之后,其实很多问题都可以避免的。