看到外卖平台这样改善数据库架构,DBA表示放心了

虢国飞 2019-02-18 11:13:16
本文根据虢国飞老师在dbaplus社群【2019年1月5日数据架构与优化沙龙上海站现场演讲内容整理而成。

下载完整PPT链接:https://pan.baidu.com/s/17UpsFdMMX-kZqYdVB017TA

 

讲师介绍
 

虢国飞

 饿了么数据技术部负责人

 

从事数据库行业十余年,专注于 MySQL、PGSQL、MSSQL等数据库领域的管理、研究和平台的研发等工作。目前在饿了么主要负责数据库和相关中间件的管理、开发和维护工作。  

 

今天我想和大家分享饿了么作为高速发展的互联网企业之一,在发展历程中数据库技术如何跟随企业发展并不断满足业务的需求,会大致介绍下数据库经历了哪些阶段,以及我们做了怎样的一些事情。

 

分享内容大致涉及到以下5点:

 

  • 数据库架构怎么满足业务、支撑业务发展;

  • 怎么提高数据库的可用性;

  • 如何对数据流进行相应的控制和保护;

  • 规模大了以后如何提高数据库运维的效率;

  • 一些个人认为重要原则的总结。

 

首先简单介绍一下饿了么的概况,点过外卖的同学应该都知道饿了么吧?

 

 

饿了么发展最快阶段也是最近四五年的事情,我是2015年进入饿了么的,那时每天才几十万的订单,服务器也不多;到了2016年时每天订单达到了几百万,商户也更多了;而2017年不管是订单、运单都是千万以上,直到现在都还在快速增长。

 

这么多数据的产生对底层数据存储是非常大的挑战,而且那个时候要在非常短的时间内应对业务的爆发性的增长,所以当时底层的技术挑战也是非常大的。

 

一、数据库架构

 

1、垂直拆分
 

 

在数据库架构方面饿了么开始的时候也是比较原始的阶段,最初是一主多从的架构;发展到后面发现订单数据库很难再满足业务往上增长的需求了;过百万之后一主不管几从都很难满足业务的需求(因为写太大),这就面临着需要拆分的情况,需要把热点业务单独拆出来,把一套数据库拆成多套,进行垂直的业务拆分。

 

数据库架构-垂直

 

拆分根据什么原则呢?又怎么来预算订单库现在的架构能承载多少的TPS和QPS呢?

 

我们当时是结合订单量、对应的QPS、TPS数据,再根据半年后的增长情况来推算出每个业务大概会产生出多少的QPS、TPS,然后结合每套集群能承载的TPS、QPS数量(基于压测),就能估算出需要拆分成什么样的结构、以及拆分后每一套(半年后)大致需要承载多少TPS、QPS。

 

当时按业务垂直拆分后,这一套方案承载了200、300万订单的规模,垂直拆分的优势是代价小见效快(业务代码改动并不大),能快速有效的支撑业务。

 

2、水平拆分
 

 

虽然按垂直架构拆分完了,但是热点的地方依然还是热点,比如说订单随着下单量的增长依然会变成很大的瓶颈。那如何打破瓶颈呢?

 

我们需要把订单这个热点再单独拆分成多套。

 

数据库架构-水平

 

也就是行业里说得比较多的“水平拆分”,把原来的一张订单表在底层拆成1000张小表,放在不同的集群上。这样即使这一个订单的量再大,也能够通过不断水平扩容机器来将压力拆分到更多的集群上,从而满足了热点的性能承载。

 

我们可以通过压测计算出每套集群能承载出多少QPS和TPS,再结合现在的业务情况就能估算出多少订单会对应产生多少的QPS和TPS,进而也能知道拆分成多少套集群能承载多少的业务量,所以也就知道了要扩多少机器才能满足半年或者一年后的业务增长。

 

如果说底层因为扩容做了分片策略,但是这个改动对业务不是透明的话,那就意味着业务需要做很多改造来适应底层的分片逻辑,一般业务是很难接受的。

 

所以,我们的水平拆分为了对业务做到透明,需要做一层代理层(我们叫DAL),代理层会帮助业务代码做到对数据库底层拆分逻辑的访问透明,业务看到的还是一张订单表,但是底层变了1000张表。

 

完成水平扩容后基本上所谓的热点也不会存在太大的瓶颈,如果再往上增长的话可以继续拆小,继续添加更多的机器来承载。

 

3、多活架构
 

 

垂直拆分之后,我们就没有性能瓶颈了吗?

 

其实机房也会成为我们的瓶颈。一般来讲企业都是租赁供应商的机房,一个机房能进多少机器不是无限的,供应商也不可能给你预留太多的位置。

 

当你服务所在的机房放不进去机器以后,你会发现虽然技术架构能满足水平扩容,但是在物理上不能再加机器了,性能瓶颈依然会到来。

 

为打破单个机房面临的容量瓶颈,我们需要扩容到更多的机房,所以我们做了“多活架构”。

 

数据库架构-多活

 

在每个机房数据库都是简单的主从架构,下单的时候北京用户可以在第一个机房下单,上海用户可以在第二个机房下单。

 

这样的话下单的高峰压力会分散到多个点去,同一套集群在两个机房都有部署,意味着承载的性能变大了。这个时候就可以在两个机房放机器,如果一个机房的机器满了放不下,我们可以把流量引到另一个机房,扩容另一边机房,这样就打破了单个机房对容量的限制。

 

我们多活逻辑是根据用户所在的位置决定他在哪个机房下单,可能他今天在北京明天在上海,下的单在不同的机房,要让用户看到所有的数据,就必须要让数据双向流通起来,所以多机房之间做数据的相互流通是数据库做多活的必备条件。

 

有很多企业做的是热备,只是在一边下单,但是另一边是backup的状态,一旦这边出现问题以后再切到那边,这样的架构并不能解决性能问题,而且资源利用率很低(有多少公司出问题真敢切?);而我们多活的架构可以在两个机房同时下单,能让性能、资源利用率和可靠性都得到明显提高。

 

数据库架构层面从垂直拆分、水平拆分到多活后,基本能满足绝大多数企业的业务发展了,这当中我们有两个组建发挥了重要的作用,也一起介绍下:

 

1)DAL

 

 

代理层(DAL),最直观的需求是能做分库分表,能做读写分离,还可以做资源隔离、连接数隔离、连接的管理等,更重要的是还能对数据库进行相应的保护。

 

外卖业务大多数人都是在中午下单,所以11点左右是饿了么的业务最高峰。

 

为了缓解数据库压力,我们会通过DAL层做消峰处理,当流量过大时我们会让用户消息做排队的处理,由此缓解对数据库的瞬间冲击。如果流量特别大的时候还可以做限流、熔断等处理。

 

还有黑白名单机制,大家了解数据库运维的话会知道,如果研发写的SQL有问题,放入到数据库里风险会比较高。如果他现在发了一个删除表的SQL命令过来就有风险,我们的DAL就会把这类黑名单SQL给拒绝。

 

更高级一点的功能是多维分表以及全局表MapTable功能。有些配置表希望在所有机房都有,DAL上就可以做GlobalTable的功能,可以保证在所有节点上都是同样的数据。

 

当然,DAL做完这些功能后,对SQL也是有一些限制的。比方说事务,下单不能跨服务片去做事务。

 

很多传统的应用业务逻辑会把很多东西包在一个事务里完成,但互联网业务应该尽量减少这种应用,在底层分片后,业务事务并不能完全通过数据库里的事务来保障。

 

可能每个表分片维度不一样,会导致数据分布到不同的机器上,这样就需要跨服务器事务一致性的保障,所以业务就不能再依赖于数据库的事务,需要通过其他机制的来保证。

 

还有Order by 、Group by会受到限制,如果你查Top10的话,DAL只会在一个分片上把Top10给到你,但并非是全局的。虽然有这些限制,但是与DAL带来的好处比是完全可以接受的。

 

2)DRC

 

 

数据同步组件DRC,实现的功能是在一边机房接受变更日志,并把变更日志传递到其他的机房去,其他机房再能把这个变更应用上。

 

为什么用DRC组件而不用MySQL原生复制呢?

 

因为我们的链路是跨机房跨地域的,上海和北京远距离的传输下,使用原生复制的话缺乏灵活性。

 

比方MySQL会产生各种各样的消息,尤其是做维护操作时要加字段的话,会产生大量的变更日志,这时直接传递就会导致网络直接堵死,在北京和上海的带宽就5~10G的情况下,一个DDL变更100G的表,会把带宽打满,这样很容易造成大的故障。

 

而且用DRC还可以做很多事情:

 

  • 比如说无用消息的过滤,MySQL平时会产生很多通知消息,但我们只需要数据变更的消息,就只需要传递变更信息;

  • 可以做数据包的压缩,还可以做维护操作的过滤。维护操作可以在两个机房同时进行,并且不希望用复制的方式来传递,这样的话避免了在维护上产生大量的变更消息,导致网络阻塞等问题;

  • 再比如说数据发生冲突了该怎么处理?还有怎么避免数据的环路。如果变更日志A写在上海机房,但是A变更传递到北京机房后,又会更新北京机房的日志,A又会通过北京机房的变更重新传回到上海机房,这样就是环路了;DRC会对相应的变更来源打上标签,这样数据就可以控制不回到自己产生的机房里。

 

二、可用性

 

下一步是怎么提高数据库的可用性。

 

整个网站的可用性是由多部分完成的,数据库只是其中的一块,所以数据库可用性要做到比整体可用性更高,比如做三个九的网站可用性,那底层需要四个九,甚至五个九的可用性来保证。

 

1、 架构
 

 

 

我们都知道物理上的故障是不可避免的,任何一台机器都有可能出现故障,任何一个设备都有可能故障,所以我们需要针对可能出现故障的地方都有相应的高可用方案。

 

EMHA

 

一台Master机器出故障的时候我们的HA基于开源MHA改造的EMHA;在每个机房里EMHA管理每个机房Master出现故障时的切换,也不光是只负责出故障的切换,还要求控制切换时间在30s左右,同时要把故障抛给其他需要通知到的地方。

 

比如代理就要知道这台Master已经挂了后新的Master是谁,所以EMHA切换时需要把消息扩散出去,让所有需要信息的组件、环节都能接受到信息。

 

这样就能达到主库挂的时候对业务的影响非常小(可能业务都没有感知),DB切换同时自动完成切组件的对接,由此来提高可用性。

 

多分片方案

 

如果对下单业务没有办法做到机器不出故障,但希望出故障时影响非常小,可以做分片方案。

 

比如说分成了10个片放10台机器上,这个时候一台机器出故障影响的是1个片,整体只会影响十分之一的业务。如果你把片分得足够小的话影响的范围会变得更小,我们对关键的业务会进行更细的分片,一个片坏了也只能影响1/n的业务。

 

异地多活

 

异地多活后一个机房出问题不会受到多大影响,因为机房间切换的时间就在几分钟内能完成,这就能让系统Online的时间大大提高。

 

另外,做重要维护的时候可以把一个机房的流量全切走,在没有流量的机房做相应的维护动作,维护完成之后再把流量切过来,然后操作另外的机房,这样风险特别高的维护操作也不用做关站处理。

 

一般大型一点的网站做一次关站维护需要的时间很长;以上这些点是从架构上能把可用性一层一层往上提。

 

下面我们再看下从故障发现和处理的角度怎么提高可用性。

 

2、 故障
 

 

可用性还有很重要的点——既然故障不可避免,那我们就要追求如何快速地发现问题,解决问题。

 

 

Trace

 

  • 全链路跟踪从应用(appid)一直下串到DB,包括有接入层、应用层、中间层、服务层、代理层、缓存层、数据库层等串联起来;

  • TraceID能提供正反向异常互推能力:ID会从上往下串,不管你在哪一层发现的问题,拿到ID就可以查看链路上哪些环节有问题(哪个环节耗时最长或者出异常),这样就可以及时地定位问题。

 

 

如果每个地方各查各的话,时间消耗是很长的,有Trace系统后,定位问题的效率会提高很多。

 

还有在数据库层面来看80%~90%的问题都是SQL问题,如果能及时获取有问题SQL,判断这个SQL的来源,并对某些非关键的问题SQL进行限流或者拦截访问的话,就能隔离问题SQL的影响,减少DB故障。

 

VDBA

 

我们在数据库层开发了一个VDBA的自动处理程序,它会不停地对所有的数据库进行扫描,根据我们制定的规则判断状态,如果发现有问题的SlowSQL会根据引起异常的程度进行限流、查杀、拒绝等操作。

 

当然VDBA能处理的不仅仅是SlowSQL,还有系统出现堵塞了,有未提交的事务、复制中断、Blocked、bionlog太大了需要清理等都能处理。很多事情让VBDA自动处理后,不仅效率提高了,也大大减少人操作的风险。

 

在故障处理时加快故障的定位时间和故障自动处理的机制后,可用性会得到明显的提升。

 

三、数据流

 

 

数据流控制也依赖于刚刚所说的一些组建。

 

作为数据的管理人员,理论上应该有自己的手段来控制什么样的数据能进入,什么样的SQL能通过,要以什么样的方式来存储等。把控不是说你写写文档就能把控住的,需要有相应强制的手段和工具。

 

每个业务访问数据库能使用多少连接、帐号权限是什么样的都需要有比较标准的控制,这样能够让所有数据在进来的时候就能够在DBA的掌控当中。

 

数据进来以后需要生产落地存储,落地后的数据也需要再传递到其他地方,这些都需要有相应的控制;比如说现在大数据要拿数据,我们就可以通过drc的消息来推送给大数据,这样就不需要再扫描数据库来拿数据了;原来的大数据通过sqoop任务都是隔天隔小时拉取数据,但现在可以做到实时的数据传递,做营销活动时可以实时看到营销的效果。

 

数据产生后还可能需要对外提供,如要把生产的数据同步到测试环境和开发环境;这个时候可以由DataBus来帮你同步数据,生产数据外传需要做数据脱敏和清洗操作(尤其是手机号、身份证号)。原来是比较麻烦的,现在研发只需要管同步的配置信息就可以了,组件会自动脱敏和清晰,非常方便,也符合安全的规范。 

 

四、运维提效

 

重点讲一下关于运维提效:在一个有上千号研发人员公司,如果只有一堆规范文档之类的来维护规则是很难把控的,因为人员有离职的、新进入的,不可能跟每个人都去宣传,所以必须要有平台来管控。

 

1、SQL治理
 

 

 

首先在SQL发布的时候,我们平台上的发布工具里面会内嵌需要遵循的标准,如果表建的时候不符合标准是没法生产提交的,这样就强制地把规则和标准变成硬性要求,SQL还可以自动实现审核也节省了DBA很多时间。

 

另外,生产一旦出现变慢的SQL后,监控系统会马上把消息push给研发,如果影响到生产运作的话会直接拒掉、查杀掉。比方我们定义超过30秒的SQL是不允许上产生的,这类SQL会被直接杀掉,这样可以大大减少生产的风险。

 

2、自助发布
 

 

 

很多公司的DBA大部分时间在审核SQL和发布SQL,而我们的SQL都是研发自助发布的,不需要DBA操心。

 

我们平台支持原生、PT执行、mm-ost执行(饿了么自行改造的数据库多机房同步发布工具),发布平台会帮他们计算好你的发布大概需要多长时间,甚至会给你判断什么时候是业务低峰(那个时候会发布比较好),这样研发对自己的发布也是比较有把控力的。

 

3、自助归档
 

 

 

归档操作也是个比较频繁的需求,一旦生产产生大量数据后,就需要做冷热数据分离,要把不需要经常用的数据搬走。原来这个操作比较费劲的,需要DBA跟在后面做很多事情,而现在只需要研发自助解决。

 

如果你的表超过1000万就需要部署归档任务了,这个时候会推送消息给研发告诉他你的表已经超过标准了,需要部署归档任务,研发自己就可以在平台上把表的归档规则填上去,完成审批后后台帮你自动地做这件事情了。

 

还有关于DB的备份和恢复,一旦数据库部署到生产后,在后台的系统里会帮你自动地部署备份和恢复任务和自动校验可用性,你还可以在平台上完成数据的回档,一旦数据刷错了、写错了通过平台就能找回。

 

4、数据保障和迁移
 

 

 

对DBA来讲需要把一个数据库从这台机器搬到另外的机器上,需要把一个大表拆分成多张小表,类似的动作就需要搬数据。

 

我们做了数据搬迁的工具,你只需要做配置就可以了,配置完成之后可以自动搬数据了,也会减少DBA很多工作量。

 

5、云实践
 

 

 

现在饿了么所有的开发测试环境都是在云上的,效率比自己做环境高很多,随时需要随时拿,用完随时释放。

 

另外还可以做弹性,弹性伸缩比较难,现在我们也没有完全实现,但正在朝着这个方向努力;我们业务的曲线是午高峰和晚高峰,这个时候流量很大,弹性调度需要在业务高峰的时候把机器加上,在业务低峰的时候把机器回收回去,提高机器的利用率。

 

云机房后面会承载我们主要的流量,云机房的好处是底层管理不需要自己负责,扩容资源比较方便,这样能提高交付的效率。

 

在云上的机房可以灰度引流,刚开始可以很少的流量去做,当我们觉得它很稳健之后就可以把流量逐步往上迁,这样能逐步把云平台的优势利用起来,把资源动态伸缩的环境利用起来,同时也能控制风险。

 

所以,现在利用云来提高运维效率是很好的手段。

 

五、建议原则

 

总结一下,从我们做这些事情里面抽取我个人感觉比较重要的点是什么呢?

 

最小可用性原则

 

不管是对帐号的处理、连接的处理、SQL的标准都应该有比较严格的限制,不能使用太多的资源,也不应该占用太多的资源。

 

最小可用性原则就是你的连接数平时只用20个,那我给你40个,有一倍波动的空间就可以了。还有帐号权限只需要增、改、查的权限,这样就不会给你删除的权限。

 

Design for Failure

 

这是我们CTO经常讲的,设计环节不管是在运维规划还是代码的环节都应该考虑接受失败的情况。

 

不管是物理层面还是架构层面的基础设施一定会出现问题,这个时候优良的架构必须要考虑应对错误情况,确保这类波动和短暂的问题能做到容错和隔离,不至于导致整体的崩溃,同时具备快速恢复的能力。

 

标准、流程、自动(助)、量化

 

一开始应该设定好标准,接着把标准拆解成流程,再把流程做成自动化、自助化的处理,进而达到维持整体标准的不变形,同时提高效率的目的,最好能做到可量化。

 

比如去年我们维护100个DB实例需要两个DBA,今年效率提升后也许一个就可以了,量化反过来也能促进运维效率的提升(可以知道哪些环节最消耗人力和资源,针对性的优化后效率就提高了)。

 

灰度、限流、熔断、隔离

 

变更是系统稳定性的很大变数,想要提高整体的可用性必须对变更环节有苛刻的限制要求,比方我们要求所有的发布必须先灰度,灰度完成之后在发一边的机房,然后再全量化,要有快速回退手段;然后程序要求有过载保护处理,具备限流、熔断和隔离等兜底措施。

 

稳定、效率、成本

 

这三点应该是企业对技术部门的核心诉求,也是有追求的技术团队不断努力的方向。

 

要方向,更要落地

 

今天介绍的内容经历过的人其实都知道每一步都不容易,对于基础设施还很薄弱的公司来讲,最重要的还是考虑自己能够用得上的,先要有落脚点,哪怕从最基础的问题开始,把问题一项一项解决。

 

然后再逐步完善,一步步的改变才能真正让用户、公司感觉到团队的价值。所以讲了这么多,最重要的还是要落地。

 

今天我的分享就到这里,谢谢大家!

 

Q&A
 

 

Q1:有一个细节我想咨询一下,DRC北京和上海是同步,如果造成两边数据不一致怎么办?因为同步肯定有时间的延迟,这种问题是怎么解决的?

 

A1:之前我也有专门讲过多活情况下数据库的方案,那里会说的比较详细一点。具体来讲在怎么同步数据、处理数据冲突是多活很重要的前提,那怎么样让数据不冲突,即使冲突了怎么受理?

 

我们多活流量分配原则是根据你所在的地理位置,同一个人在下单的时候在同一地方只会打到同一机房,所以下单当时产生的数据是在一个点一个机房的,而且其他操作我们就把用户放到这个机房里面,不可能说在这边下完单在另外一边支付,这在业务设计规则上是不允许的。业务设计规则上做到限制,下单、支付必须在一个机房完成。

 

之后在底层会做相应的保护,数据库会设计每个机房的自身因子一样但起始值不一样,比方一个机房是1,一个机房是2,那边永远是单数,这边永远是双数,这样数据库自己产生的主键值是不会冲突的。

 

还有像抢红包这种事情在两个机房都会抢,这样的场景可能会造成冲突。这个时候可以根据最后修改时间做为有效值覆盖另外的值。

 

所以首先是有设计原则,原则被突破后会针对冲突有相应的技术处理标准;两个机房还会有组件保护机制,还有数据校验,一旦发现有数据冲突的话会告警;有一些冲突系统可以自动处理,还有有些冲突必须要业务识别,因为不排除业务有违反设计的情况出现,这样的冲突会抛给业务来处理,一旦大家通过冲突告警梳理后都按标准遵循这些规则,有冲突的概率是非常小的。

 

之前我们发现有冲突的情况大部分是业务违反了设计原则,比如说UK的表唯一值在这边产生也在那边产生,这样的话数据就冲突了;如果业务能够接受也可以做过滤,不能接受就会要求改造。

 

Q2:如果我在上海下单,现在我去北京查不到单,这种情况下会有延迟吗?

 

A2:这个情况一般不会出现,我们的流量分流机制会保证用户当前的所有操作只会在一个机房发生,而且正常DRC的延迟一般是秒级别,跨机房延迟现在基本在一秒之内,你从上海去北京肯定操作这个时间。

 

还有用户在一个机房下单一般只需要一个机房的数据就足够了,我们会在底层保证数据一致的;数据校验是分钟级别的,数据发生问题的话会马上发现。

 

Q3:像点餐也就3、4个小时的高峰,能介绍一下这样的业务弹性场景吗?

 

A3:目前我们在数据层并不能做到弹性,但是在其他无状态业务资源上是可以的,我们也还在研究当中。比方说现在有一套承载业务的集群,我们的业务高峰在10点开始,在9点的时候会把资源加上去,原来有10台,为了满足业务高峰会加到50台,在9点之前把资源加进集群里去。用完之后到下午3点可以再退出去。

 

Q4:闲置的时候做什么其他利用?每天只买云资源的时间段计费?

 

A4:这就是利用云的好处,退出来以后云就不会计费了,可以让云回收。但如果是自有机房自己的机房的话要看自己的需求,像阿里现在有在做类似的方案,会在业务低峰时候在业务机器上跑大数据计算提高资源利用率。可以每天只在买云资源的时间段计费,但是控制需要做得比较好,需要的时候能加上去,不需要的时候能退出来。

活动预告