货拉拉技术

适配混合云,货拉拉的数据库中间件建设之路

低延迟指标其实包含了2个维度的考量:一个RT平均值的小,一个RT抖动的小。做为数据库中间件,稳定性肯定第一位的,我们一般选择RT抖动做为衡量稳定性的核心指标。在技术上我们选择了Netty NIO的多路复用框架以及Java16 ZGC来实现这个维度指标的提升。

  • 高可用—99.999%服务可用

高可用的量化指标一般用X个9来表达,X个9表示在系统1年时间的使用过程中,系统可以正常使用时间与总时间(1年)之比。一般企业级软件要求是4个9,也就是全年允许挂50分钟,5个9的话就是全年只能挂5分钟,而我们的数据库中间件上线16个月一直保持0故障,算是我们做的比较满意的部分。

  • 低成本— 成本占用不到RDS的5%

所谓低成本,自己做的蛋糕肯定比买的便宜。当然这只是开个玩笑,这个肯定不能这么考虑。这里涉及到三个成本,软硬件成本+运维成本+研发成本。前两个相信大家都比较熟悉了。

我主要分享下对第三个成本的观点:和过去大家什么都想要自己开发相反,现在我们往往会过分夸大自研投入的成本,而直接放弃自研的选项。在中间件领域开源氛围浓郁的大环境下,我们其实比以往更容易培养中间件人才,利用社区资源我们也更有把握解决相关的难题,因此研发的成本并不会太高。而我们在研发实践后也能够反馈社区,形成正循环,这是一个双赢选择。

3、可运维性与技术设计

可运维性是衡量一个中间件产品设计是否“好用”的重要维度。虽然是针对产品上线后的生命周期,却是在设计阶段决定。可运维性的内容比较丰富,在总结反思我们产品的建设过程后,总结了以下4点。

1)面向故障设计

  • 非核心依赖可降级

  • 核心依赖做好冗余

  • 建设系统“自证清白”能力

  • 最后一道防线手动SOP

面向故障设计应该是大家比较熟悉的概念了。特别是在体量上来以后,故障就变成是必然,只有一台机器的时候我们说今天它有可能坏,而我们有10000台的时候,我们会肯定的说今天必然要坏一台。特别值得一提的是,我们有时候会神化云环境的稳定性,以为上云了就不会有故障了。云环境的稳定性不是说不出故障,而是有一套完整机制来故障恢复,是一种反脆弱的设计。而有些故障特别是硬件故障,并没有快速的恢复办法,比如交换机坏了,光发现问题就需要不短的时间,更何况恢复。所以无论是什么环境,我们都要设计好故障的预案。

在数据库中间件的场景里,可降级的依赖一般指影响旁路的功能,动态修改配置,监控等。不可降级的部分包括关键数据,硬件设备等。对于数据我们肯定要做好备份,而硬件问题换个角度其实更多是单节点故障,集群化是非常合适的解决方案。

在一个大的系统里发生故障,往往是到处都冒烟,但就是找不到真正出问题的点。这个时候“自证清白”就显的非常重要,如果所有组件能快速确定自己的状态,故障点清晰可见了。当然“自证清白”并不是那么容易的,真实的情况往往是大盘故障了,所有人都摸不清自己的模块是不是有问题,然后各种看日志,查监控,可即使这样的付出,还是很难得出结论。“自证清白”是一个重要又非常有挑战的能力,需要对本领域有非常深的理解,同时又需要监控报警等基础能力配合。在数据库中间件这里,主要关注内部的工作线程负载,外部表现的RT,以及网络的丢包这三个关键数据。

手动SOP属于兜底手段了,比如数据库中间件保留了本地文件启动能力。

2)产品标准化

  • 针对不同使用特征,分级群隔离

  • 使用统一硬软件标准

  • 尽量复用企业现有标准

  • 接入标准化

  • 功能简单正交

标准化是一种追求全局最优的设计理念。

一个是功能设计的标准化。作为基础中间件,我们的功能必须是简单正交的,这样就能够在实现功能的时候更专注保证质量,同时也留出进一步的编排的空间。举个例子,我们提供了限制指定SQL pqs的功能,也提供了上报异常SQL(类似SQL长度过长)的能力。这个时候如果在上层控制面做一个自动限制异常SQL的能力,就会非常顺利,即容易实现也可以方便的灰度回滚。而我们如果一开始就在数据库中间件这里憋大招,就很难做到这样自如,反而很有可能会因为功能相互影响,复杂度太高,引入bug等破坏迭代节奏。

另一个是外部依赖的标准化。比如尽量使用公司统一的ECS机型,能够最大的避免机器没库存的问题。使用公司统一的基础组件,能够免去额外维护的成本,同时提高的产品开发效率和稳定性。

最后是用户使用的标准化。我们应该避免提供太多的不确定给用户,而是只给一个标准答案。我们应该统一出一套最佳实践来,在这个基础上做好配套的优化。这样用户可以轻松接入,并把这个过程轻松复制给其他人。提升效率的同时,也避免了因不合理的使用姿势可能引发的故障。

3)排障智能化

  • 服务监控,系统监控,外部依赖监控

  • 链路追踪

  • 自动报警

监控的重要性毋庸置疑,我们需要在一开始就把监控埋点纳入到开发计划中,而不是在某次故障后,才开始补充各种监控指标。

监控埋点的要点在于能够全面覆盖产品主要流程,包括数据流和控制流,正常流程和异常流程。这个时候容易进入的误区是,过早的对埋点数据做处理。比如内部有一个工作队列,比起根据某个阈值打一个队列是否繁忙的点,更合适的做法是直接打出队列长度,这样我们可以使用图表来展示队列的繁忙度变化,也可以灵活的设置告警。告警可是0值的空闲异常告警,也可以是100的繁忙异常告警。

无论是报警,链路还是监控都是要服务于排障的,所以我们要站在的方便排障的角度,来设计和验收这些指标埋点。当我们怀疑某处的打点是否必要时,可以看看是否对线上排障有帮助。

线上排障我们说要“智能化”,其实我们真正想做到的是“傻瓜式”的排障预案。能够在故障发生第一时间收到报警,然后通过链路追踪工具直接找到根因,最后通过预案按部就班处理。

4)管理自动化

  • 最终“消灭”人工环节

  • 用户自助

自动化提升效率是我们的共识,自动化能够帮助我们从大量的重复劳动中解放出来,提升工作效率。

那么如果量不是那么大,也不是那么重复的部分呢?是否还有必要自动化,比如审核等动作。

我认为是必要的。当我们发现某个部分不能自动化,想用人工的方式“糊弄”过去的时候,往往这就是我们没有考虑清楚的地方,不彻底解决这个问题,这个问题就会像狗皮膏药一样一直拉低我们的用户体验。我们之前就遇到过这样情况:新建逻辑库可能会影响老逻辑库,怕用户填错信息导致异常,我们就设置了好几道人工审核来review。而事实上人工审核并没有解决问题,反而阻碍了整体的自动化。最后我们重新设计了逻辑库的配置隔离和回滚能力,自然自动化也不成问题了。

“消灭”人工环节很大程度是在扫清我们产品的潜在问题,是我们假装看不见的地方。

用户自助也是类似的,一个能够自助的系统意味什么呢?意味我们的系统是健壮稳定的,我们有良好的隔离设计,有强大的容错能力。把自助当成目标,是建设更好的产品的良好推动力。

总的来说,可运维性影响软件后期所要投入的“隐性成本”,是一种高回报的长远的投资。

四、解决的问题及收益

这里总结下自建数据库中间件带来的收益。

  • 提升研发效率;

  • 提升运维效率;

  • 提升系统稳定性;

  • 保持厂商中立,实现“云自由”。