欢迎关注微信公众号:石杉的架构笔记(id:shishan100)面试
个人新课**《C2C 电商系统微服务架构120天实战训练营》在公众号儒猿技术窝**上线了,感兴趣的同窗,能够点击下方连接了解详情:算法
**“**本文聊一下笔者几年前所带的团队负责的多个项目中的其中一个,用这个项目来聊聊一个亿级流量系统架构演进的过程。性能优化
首先简单介绍一下项目背景,公司对合做商家提供一个付费级产品,这个商业产品背后涉及到数百人的研发团队协做开发,包括各类业务系统来提供不少强大的业务功能,同时在整个平台中包含了一个相当重要的核心数据产品,这个数据产品的定位是全方位支持用户的业务经营和快速决策。服务器
这篇文章就聊聊这个数据产品背后对应的一套大型商家数据平台,看看这个平台在分布式、高并发、高可用、高性能、海量数据等技术挑战下的架构演进历程。微信
由于整套系统规模过于庞大,涉及研发人员不少,持续时间很长,文章难以表述出其中各类详细的技术细节以及方案,所以本文主要从总体架构演进的角度来阐述。markdown
至于选择这个商家数据平台项目来聊架构演进过程,是由于这个平台基本跟业务耦合度较低,不像咱们负责过的C端类的电商平台以及其余业务类平台有那么重的业务在里面,文章能够专一阐述技术架构的演进,不须要牵扯太多的业务细节。架构
此外,这个平台项目在笔者带的团队负责过的众多项目中,相对算比较简单的,可是先后又涉及到各类架构的演进过程,所以很适合经过文字的形式来展示出来。并发
下面几点,是这个数据产品最核心的业务流程:app
基本上用户在业务系统使用过程当中,只要数据一有变更,立马就反馈到各类数据报表中,用户立马就能够看到数据报表中的各类变化,进而快速的指导本身的决策和管理。
整个过程,你们看看下面的图就明白了。
看着上面那张图好像很是的简单,是否是?
彷佛数据平台只要想个办法把业务系统的数据采集过来,接着放在MySQL的各类表里,直接咔嚓一下运行100多个几百行的大SQL,而后SQL运行结果再写到另一些MySQL的表里做为报表数据,接着用户直接点击报表页面查询MySQL里的报表数据,就能够了!
其实任何一个系统从0到1的过程,都是比较low的,刚开始为了快速开发出来这个数据平台,还真的就是用了这种架构来开发,你们看下面的图。
其实在刚开始业务量很小,请求量很小,数据量很小的时候,上面那种架构也没啥问题,还挺简单的。
直接基于本身研发的数据库binlog采集中间件(这个是另一套复杂系统了,不在本文讨论的范围里,之后有机会能够聊聊),感知各个业务系统的数据库中的数据变动,毫秒级同步到数据平台本身的MySQL库里;
接着数据平台里作一些定时调度任务,每隔几秒钟就运行上百个复杂大SQL,计算各类报表的数据并将结果存储到MySQL库中;
最后用户只要对报表刷新一下,立马就能够从MySQL库里查到最新的报表数据。
基本上在无任何技术挑战的前提下,这套简易架构运行的会很顺畅,效果很好。然而,事情每每不是咱们想的那么简单的,由于你们都知道国内那些互联网巨头公司最大的优点和资源之一,就是有丰富以及海量的C端用户以及B端的合做商家。
论C端用户,任何一个互联网巨头推出一个新的C端产品,极可能迅速就是上亿用户量;
论B端商家,任何一个互联网巨头若是打B端市场,凭借巨大的影响力以及合做资源,极可能迅速就能够聚拢数十万,乃至上百万的付费B端用户。
所以不幸的是,接下来的一两年内,这套系统将要面临业务的高速增加带来的巨大技术挑战和压力。
其实跟不少大型系统遇到的第一个技术挑战同样,这套系统遇到的第一个大问题,就是海量数据的存储。
你一个系统刚开始上线也许就几十个商家用,接着随着大家产品的销售持续大力推广,可能几个月内就会聚拢起来十万级别的用户。
这些用户天天都会大量的使用你提供的产品,进而天天都会产生大量的数据,你们能够想象一下,在数十万规模的商家用户使用场景下,天天你新增的数据量大概会是几千万条数据,**记住,这但是天天新增的数据!**这将会给上面你看到的那个很low的架构带来巨大的压力。
若是你在负责上面那套系统,结果慢慢的发现,天天都要涌入MySQL几千万条数据,这种现象是使人感到崩溃的,由于你的MySQL中的单表数据量会迅速膨胀,很快就会达到单表几亿条数据,甚至是数十亿条数据,而后你对那些怪兽同样的大表运行几百行乃至上千行的SQL?其中包含了N层嵌套查询以及N个各类多表链接?
我跟你打赌,若是你愿意试一下,你会发现你的数据平台系统直接卡死,由于一个大SQL可能都要几个小时才能跑完。而后MySQL的cpu负载压力直接100%,弄很差就把MySQL数据库服务器给搞宕机了。
因此这就是第一个技术挑战,数据量愈来愈大,SQL跑的愈来愈慢,MySQL服务器压力愈来愈大。
咱们当时而言,已经看到了业务的快速增加,所以绝对要先业务一步来重构系统架构,不能让上述状况发生,第一次架构重构,势在必行!
其实在几年前咱们作这个项目的时候,大数据技术已经在国内开始运用的不错了,并且尤为在一些大型互联网公司内,咱们基本上都运用大数据技术支撑过不少生产环境的项目了,在大数据这块技术的经验积累,也是足够的。
针对这个数据产品的需求,咱们彻底能够作到,将昨天以及昨天之前的数据都放在大数据存储中,进行离线存储和离线计算,而后只有今天的数据是实时的采集的。
所以在这种技术挑战下,第一次架构重构的核心要义,就是将离线计算与实时计算进行拆分。
你们看上面那张图,新的架构之下,分为了离线与实时两条计算链路。
**一条是离线计算链路:**天天凌晨,咱们将业务系统MySQL库中的昨天之前的数据,做为离线数据导入Hadoop HDFS中进行离线存储,而后凌晨就基于Hive / Spark对离线存储中的数据进行离线计算。
若是有同窗不清楚大数据的知识,能够参加我以前写的一篇文章:兄弟,用大白话告诉你小白都能看懂的Hadoop架构原理。Hadoop与Spark做为世界上最优秀、运用最普遍的大数据技术,自然适合PB级海量数据的分布式存储和分布式计算。
在离线计算链路全面采用大数据相关技术来支撑事后,完美解决了海量数据的存储,哪怕你一天进来上亿条数据都没事,分布式存储能够随时扩容,同时基于分布式计算技术自然适合海量数据的离线计算。
即便是天天凌晨耗费几个小时将昨天之前的数据完成计算,这个也没事,由于凌晨通常是没人看这个数据的,因此主要在人家早上8点上班之前,完成数据计算就能够了。
**另一条是实时计算链路:**天天零点事后,当天最新的数据变动,所有仍是走以前的老路子,秒级同步业务库的数据到数据平台存储中,接着就是数据平台系统定时运行大量的SQL进行计算。同时在天天零点的时候,还会从数据平台的存储中清理掉昨天的数据,仅仅保留当天一天的数据而已。
实时计算链路最大的改变,就是仅仅在数据平台的本地存储中保留当天一天的数据而已,这样就大幅度下降了要放在MySQL中的数据量了。
举个例子:好比一天就几千万条数据放在MySQL里,那么单表数据量被维持在了千万的级别上,此时若是对SQL对应索引以及优化到极致以后,勉强仍是能够在几十秒内完成全部报表的计算。
可是若是仅仅只是作到上面的架构,仍是只能暂时性的缓解系统架构的压力,由于业务还在加速狂飙,继续增加。
你总是指望单日的数据量在千万级别,怎么可能?业务是不会给你这个机会的。很快就能够预见到单日数据量将会达到几亿,甚至十亿的级别。
若是一旦单日数据量达到了数十亿的级别,单表数据量上亿,你再怎么优化SQL性能,有没法保证100多个几百行的复杂SQL能够快速的运行完毕了。
到时候又会回到最初的问题,SQL计算过慢会致使数据平台核心系统卡死,甚至给MySQL服务器过大压力,CPU 100%负载后宕机。
并且此外还有另一个问题,那就是单个MySQL数据库服务器的存储容量是有限的,若是一旦单日数据量达到甚至超过了单台MySQL数据库服务器的存储极限,那么此时也会致使单台MySQL数据库没法容纳全部的数据了,这也是一个很大的问题!
第二次架构重构,势在必行!
7、大数据领域的实时计算技术的缺陷
在几年前作这个项目的背景下,当时可供选择的大数据领域的实时计算技术,主要仍是Storm,算是比较成熟的一个技术,另外就是Spark生态里的Spark Streaming。当时可没有什么如今较火的Flink、Druid等技术。
在仔细调研了一番事后发现,根本没有任何一个大数据领域的实时计算技术能够支撑这个需求。
由于Storm是不支持SQL的,并且即便勉强你让他支持了,他的SQL支持也会很弱,彻底不可能运行几百行甚至上千行的复杂SQL在这种流式计算引擎上的执行。
Spark Streaming也是同理,当时功能仍是比较弱小的,虽然能够支持简单SQL的执行,可是彻底没法支持这种复杂SQL的精准运算。
所以很不幸的是,在当时的技术背景下,遇到的这个实时数据运算的痛点,没有任何开源的技术是能够解决的。必须得本身根据业务的具体场景,来从0开始定制开发本身的一套数据平台系统架构。
首先咱们要先解决第一个痛点,就是一旦单台数据库服务器没法存储下当日的数据,该怎么办?
第一个首选的方案固然就是分库分表了。咱们须要将一个库拆分为多库,不用的库放在不一样的数据库服务器上,同时每一个库里放多张表。
采用这套分库分表架构以后,能够作到每一个数据库服务器放一部分的数据,并且随着数据量日益增加,能够不断地增长更多的数据库服务器来容纳更多的数据,作到按需扩容。
同时,每一个库里单表分为多表,这样能够保证单表数据量不会太大,控制单表的数据量在几百万的量级,基本上性能优化到极致的SQL语句跑起来效率仍是不错的,秒级出结果是能够作到的。
一样,给你们来一张图,你们直观的感觉一下:
此时分库分表以后,又面临着另一个问题,就是如今若是对每一个数据库服务器又是写入又是读取的话,会致使数据库服务器的CPU负载和IO负载很是的高!
为何这么说呢?由于在此时写数据库的每秒并发已经达到几千了,同时还频繁的运行那种超大SQL来查询数据,数据库服务器的CPU运算会极其的繁忙。
所以咱们将MySQL作了读写分离的部署,每一个主数据库服务器都挂了多个从数据库服务器,写只能写入主库,查能够从从库来查。
你们一块儿来看看下面这张图:
可是光是作到这一点仍是不够的,由于其实在生产环境发现,哪怕单表数据量限制在了几百万的级别,你运行几百个几百行复杂SQL,也要几十秒甚至几分钟的时间,这个时效性对付费级的产品已经有点没法接受,产品提出的极致性能要求是,秒级!
所以对上述系统架构,咱们再次作了架构的优化,在数据平台中嵌入了本身纯自研的滑动窗口计算引擎,核心思想以下:
经过这套滑动窗口的计算引擎,咱们直接将系统计算性能提高了几十倍,基本上每一个滑动窗口的数据只要几秒钟就能够完成所有报表的计算,至关于一会儿把最终呈现给用户的实时数据的时效性提高到了几秒钟,而不是几十秒。
一样,你们看看下面的图。
实时计算链路的性能问题经过自研滑动窗口计算引擎来解决了,可是离线计算链路此时又出现了性能问题。。。
由于天天凌晨从业务库中离线导入的是历史全量数据,接着须要在凌晨针对百亿量级的全量数据,运行不少复杂的上千行复杂SQL来进行运算,当数据量达到百亿以后,这个过程耗时很长,有时候要从凌晨一直计算到上午。
关键问题就在于,离线计算链路,天天都是导入全量数据来进行计算,这就很坑了。
之因此这么作,是由于从业务库同步数据时,天天都涉及到数据的更新操做,而hadoop里的数据是无法跟业务库那样来进行更新的,所以最开始都是天天导入全量历史数据,做为一个最新快照来进行全量计算。
在这里,咱们对离线计算链路进行了优化,**主要就是全量计算转增量计算:**天天数据在导入hadoop以后,都会针对数据的业务时间戳来分析和提取出来天天变动过的增量数据,将这些增量数据放入独立的增量数据表中。
同时须要根据具体的业务需求,自动分析数据计算的基础血缘关系,有可能增量数据须要与部分全量数据混合才能完成计算,此时可能会提取部分全量历史数据,合并完成计算。计算完成以后,将计算结果与历史计算结果进行合并。
在完成这个全量计算转增量计算的过程以后,离线计算链路在凌晨基本上百亿级别的数据量,只要对昨天的增量数据花费一两个小时完成计算以后,就能够完成离线计算的所有任务,性能相较于全量计算提高至少十倍以上。
到此为止,就是这套系统在最初一段时间作出来的一套架构,不算太复杂,还有不少缺陷,不完美,可是在当时的业务背景下效果至关的不错。
在这套架构对应的早期业务背景下,天天新增数据大概是亿级左右,可是分库分表以后,单表数据量在百万级别,单台数据库服务器的高峰期写入压力在2000/s,查询压力在100/s,数据库集群承载的总高峰写入压力在1万/s,查询压力在500/s,有须要还能够随时扩容更多的数据库服务器,承载更多的数据量,更高的写入并发与查询并发。
并且,由于作了读写分离,所以每一个数据库服务器的CPU负载和IO负载都不会在高峰期打满,避免数据库服务器的负载太高。
而基于滑动时间窗口的自研计算引擎,能够保证当天更新的实时数据主要几秒钟就能够完成一个微批次的计算,反馈到用户看到的数据报表中。
同时这套引擎自行管理着计算的状态与日志,若是出现某个窗口的计算失败、系统宕机、计算超时,等各类异常的状况,这个套引擎能够自动重试与恢复。
此外,昨天之前的海量数据都是走Hadoop与Spark生态的离线存储与计算。通过性能优化以后,天天凌晨花费一两个小时,算好昨天之前全部的数据便可。
最后实时与离线的计算结果在同一个MySQL数据库中融合,此时用户若是对业务系统作出操做,实时数据报表在几秒后就会刷新,若是要看昨天之前的数据能够随时选择时间范围查看便可,暂时性是知足了业务的需求。
早期的几个月里,日增上亿数据,离线与实时两条链路中的总体数据量级达到了百亿级别,不管是存储扩容,仍是高效计算,这套架构基本是撑住了。
这个大型系统架构演进实践是一个系列的文章,将会包含不少篇文章,由于一个大型的系统架构演进的过程,会持续很长时间,作出不少次的架构升级与重构,不断的解决日益增加的技术挑战,最终完美的抗住海量数据、高并发、高性能、高可用等场景。
下一篇文章会说说下一步是如何将数据平台系统重构为一套高可用高容错的分布式系统架构的,来解决单点故障、单系统CPU负载太高、自动故障转移、自动数据容错等相关的问题。包括以后还会有多篇文章涉及到咱们自研的更加复杂的支撑高并发、高可用、高性能、海量数据的平台架构。
上一篇文章写了一个分布式锁的高并发优化的文章,具体参见:每秒上千订单场景下的分布式锁高并发优化实践!。收到了你们不少的提问,其实最终都是一个问题:
针对那篇文章里的用分布式锁的分段加锁的方式,解决库存超卖问题,那若是一个分段的库存不知足要购买的数量,怎么办?
第一,我当时文章里提了一句,可能没写太详细,若是一个分段库存不足,要锁其余的分段,进行合并扣减,若是你作分段加锁,那就是这样的,很麻烦。
若是你们去看看Java 8里的LongAdder的源码,他的分段加锁的优化,也是如此的麻烦,要作段迁移。
第二,我在那篇文章里反复强调了一下,不要对号入座,由于实际的电商库存超卖问题,有不少其余的技术手段,咱们就用的是其余的方案,不是这个方案,之后有机会给你们专门讲如何解决电商库存超卖问题。
那篇文章仅仅是用那个例做为一个业务案例而已,阐述一下分布式锁的并发问题,以及高并发的优化手段,方便你们来理解那个意思,仅此而已。
第三,最后再强调一下,你们关注分段加锁的思想就好,切记不要对号入座,不要关注过多在库存超卖业务上了。
《亿级流量系统架构之如何设计高容错分布式计算系统》
《亿级流量系统架构之如何设计承载百亿流量的高性能架构》
《亿级流量系统架构之如何设计每秒数十万查询的高并发架构》
《亿级流量系统架构之如何设计全链路99.99%高可用架构》
若有收获,请帮忙转发,您的鼓励是做者最大的动力,谢谢!
一大波微服务、分布式、高并发、高可用的****原创系列
文章正在路上,欢迎扫描下方二维码,持续关注:
石杉的架构笔记(id:shishan100)
十余年BAT架构经验倾囊相授
**推荐阅读:
二、【双11狂欢的背后】微服务注册中心如何承载大型系统的千万级访问?
三、【性能优化之道】每秒上万并发下的Spring Cloud参数优化实战
六、大规模集群下Hadoop NameNode如何承载每秒上千次的高并发访问
七、【性能优化的秘密】Hadoop如何将TB级大文件的上传性能优化上百倍
八、拜托,面试请不要再问我TCC分布式事务的实现原理坑爹呀!
九、【坑爹呀!】最终一致性分布式事务如何保障实际生产中99.99%高可用?
十、拜托,面试请不要再问我Redis分布式锁的实现原理!**