MapReduce经典设计,给了我们哪些架构启示?(第85讲,超长文)

《架构师之路:架构设计中的100个知识点》

85.MapReduce架构启示

 

第一部分:MapReduce究竟解决什么问题。

 

很多时候,定义清楚问题比解决问题更难。

 

什么是MapReduce?

它不是一个产品,而是一种解决问题的思路,它有多个工程实现,Google在论文中也给出了它自己的工程架构实现。

 

MapReduce这个编程模型解决什么问题?

能够用分治法解决的问题,例如:

1. 网页抓取;

2. 日志处理;

3. 索引倒排;

4. 查询请求汇总;

5…

画外音:现实中有许多基于分治的应用需求。

 

为什么是Google,发明了这个模型?

Google网页抓取,分析,倒排的多个应用场景,当时的技术体系,解决不了Google大数据量高并发量的需求,Google被迫进行技术创新,思考出了这个模型。

画外音:谁痛谁想办法。

 

为什么MapReduce对“能够用分治法解决的问题”特别有效?

分治法,是将一个大规模的问题,分解成多个小规模的问题(分),多个小规模问题解决,再统筹小问题的解(合),就能够解决大规模的问题。

 

Google MapReduce为什么能够成功?

Google为了方便用户使用系统,提供给了用户很少的接口,去解决复杂的问题。
1. Map函数接口:处理一个基于key/value(后简称kv)的成对(pair)数据集合,同时也输出基于kv的数据集合;
2. Reduce函数接口:用来合并Map输出的kv数据集合;

画外音:MapReduce系统架构,能在大规模普通PC集群上实现并行处理,和GFS等典型的互联网架构类似。


用户仅仅关注少量接口,不用关心并行、容错、数据分布、负载均衡等细节,又能够解决很多实际的问题,还有这等好事!

 

能不能举一个例子,说明下MapReduce的Map函数与Reduce函数是如何解决实际问题的?

 

举例:假设要统计大量文档中单词出现的个数。

 

Map

输入KV:pair(文档名称,文档内容)

输出KV:pair(单词,1)

画外音:一个单词出现一次,就输出一个1。

 

Reduce

输入KV:pair(单词,1)

输入KV:pair(单词,总计数)

 

以下是一段伪代码:
Map(list<pair($doc_name, $doc_content)>){

    foreach(pair in list)

        foreach($word in $doc_content)

            echo pair($word, 1); // 输出list<k,v>

}

画外音:如果有多个Map进程,输入可以是一个pair,不是一个list。

 

Reduce(list<pair($word, $count)>){// 大量(单词,1)

    map<string,int> result;

    foreach(pair in list)

        result[$word] += $count;

    foreach($keyin result)

        echo pair($key, result[$key]); // 输出list<k,v>

}

画外音:即使有多个Reduce进程,输入也是list<pair>,因为它的输入是Map的输出。

 

最早在单机的体系下计算,输入数据量巨大的时候,处理很慢。如何能够在短时间内完成处理,很容易想到的思路是,将这些计算分布在成百上千的主机上,但此时,会遇到各种复杂的问题,例如:

1. 并行计算

2. 数据分发

3. 错误处理

4. 集群通讯

5…

 

这些综合到一起,就成为了一个困难的问题,这也是Google MapReduce工程架构要解决的问题。

 

第二部分:MapReduce的核心优化思路。

 

为了解决上述场景遇到的各种复杂问题,MapReduce的核心优化思路是:

1. 并行;

2. 先分再合;

 

下图简述了MR计算“词频统计”的过程。

图片

从左到右四个部分,分别是:

1. 输入文件;

2. 分:M个并行的map计算实例;

3. 合:R个并行的reduce计算实例;

4. 输出结果;

 

先看最后一步,reduce输出最终结果。

图片

可以看到,R个reduce实例并发进行处理,直接输出最后的计数结果。

实例1输出:(a, 256)(able, 128)(emacs, 1)

实例2输出:(f*ck, 32768) (coding, 65535)

实例3输出:(vim,65535)(x, 16)(zero, 258)

画外音:这就是总结果,可以看到vim比emacs受欢迎很多。

 

需要理解的是,由于这是业务计算的最终结果,一个单词的计数不会出现在两个实例里。即:如果(a, 256)出现在了实例1的输出里,就一定不会出现在其他实例的输出里。

画外音:否则的话,还需要合并,就不是最终结果了。

 

再看中间步骤,map到reduce的过程。

图片

可以看到,M个map实例的输出,会作为R个reduce实例的输入。

 

潜在问题一:每个map都有可能输出(a, 1),而最终结果(a, 256)必须由一个reduce输出,那如何保证每个map输出的同一个key,落到同一个reduce上去呢?

这就是“分区函数”的作用。

 

什么是分区函数?

分区函数,是使用MapReduce的用户需要实现的,决定map输出的每一个key应当落到哪个reduce上的函数。

画外音:如果用户没有实现,会使用默认分区函数。

 

以词频统计的应用为例,分区函数可能是:

(1) 以[a-g]开头的key落到第一个reduce实例;

(2) 以[h-n]开头的key落到第二个reduce实例;

(3) 以[o-z]开头的key落到第三个reduce实例;

画外音:有点像数据库水平切分的“范围法”。

 

分区函数实现要点是什么?

为了保证每一个reduce实例都能够差不多时间结束工作任务,分区函数的实现要点是:尽量负载均衡。

画外音:即数据均匀分摊。

 

上述词频统计的分区函数,就不是负载均衡的,有些reduce实例处理的单词多,有些reduce处理的单词少,这样就可能出现,所有reduce实例都处理结束,最后等待一个长尾reduce的情况。

 

对于词频统计,负载更为均衡的分区函数为:

hash(key) % 3

画外音:有点像数据库水平切分的“哈希法”。

 

潜在问题二:每个map都有可能输出多个(a, 1),这样无形中增大了网络带宽资源,以及reduce的计算资源,有没有办法进行优化呢?

这就是“合并函数”的作用。

 

什么是合并函数?

有时,map产生的中间key的重复数据比重很大,可以提供给用户一个自定义函数,在一个map实例完成工作后,本地就做一次合并,这样网络传输与reduce计算资源都能节省很多。

 

合并函数在每个map任务结束前都会执行一次,一般来说,合并函数与reduce函数是一样的,区别是:

1. 合并函数执行map实例本地数据合并;

2. reduce函数执行最终的合并,会收集多个map实例的数据;

 

对于词频统计应用,合并函数可以将:

一个map实例的多个(a, 1)合并成一个(a, $count)输出。

 

最后看第一个个步骤,输入文件到map的过程。

图片

潜在问题三:如何确定文件到map的输入呢?

随意即可,只要负载均衡,均匀切分输入文件大小就行,不用管分到哪个map实例。

画外音:无论分到那个map都能正确处理。

 

结论,Google MapReduce实施了一系列的优化:

1. 分区函数:保证不同map输出的相同key,落到同一个reduce里;

2. 合并函数:在map结束时,对相同key的多个输出做本地合并,节省总体资源;

3. 输入文件到map如何切分:随意,切分均匀就行;

 

第三部分:MapReduce的工程架构实践。

 

上述优化后的执行流程,Google MapReduce通过怎样的工程架构实现的呢?

图片

先看下总体架构图,有个直观的印象。

 

用户使用GoogleMR系统,必须输入的是什么?

1. 输入数据,必选

画外音:否则系统处理啥。

2. map函数,必选

3. reduce函数,必选

画外音:分治法,分与合的业务逻辑。

4. 分区函数,必选

画外音:保证同一个key,在合并阶段,必须落到同一个reduce上,系统提供默认hash(key)法。

5. 合并函数,可选

画外音:看用户是否需要在map结束阶段进行优化。

 

用户提供各个输入后,GoogleMR的执行流程是什么?

画外音:不妨假设,用户设置了M个map节点,R个reduce节点;例如:M=500,R=200。

(1) 在集群中创建大量可执行实例副本(fork);

 

(2) 这些副本中有一个master,其他均为worker,任务的分配由master完成, M个map实例和R个reduce实例由worker完成;

 

(3) 将输入数据分成M份,然后被分配到map任务的worker,从其中一份读取输入数据,执行用户的map函数处理,并在本地内存生成临时数据;

 

(4) 本地内存临时数据,通过分区函数,被分成R份,周期性的写到本地磁盘,由master调度,传给被分配到reduce任务的worker;

 

(5) 负责reduce任务的worker,从远程读取多个map输出的数据,执行用户的reduce函数处理,处理结果写入输出文件;

画外音:可能对key要进行外部排序。

 

(6) 所有map和reduce的worker都结束工作后,master唤醒用户程序,MapReduce调用返回,结果被输出到了R个文件中。

 

GoogleMR系统里的master和worker是啥?

 

(1) master:单点master会存储一些元数据,监控所有map与reduce的状态,记录哪个数据要给哪个map,哪个数据要给哪个reduce,掌控全局视野,做中控;

画外音:是不是和GFS的master非常像?

 

(2) worker:多个worker进行业务逻辑处理,具体一个worker是用来执行map还是reduce,是由master调度的;

画外音:是不是和工作线程池非常像?这里的worker是分布在多台机器上的而已。

 

master的高可用是如何保证的?

一个简单的方法是,将元数据固化到磁盘上,用一个shadow-master来做高可用。

画外音:GFS不就是这么干的么?

 

然而现实情况是:没有将元数据固化到磁盘上,元数据被存放在master的内存里用以提高工作效率,当master挂掉后,通知用户“任务执行失败”,让其选择重新执行。

画外音:

(1) 单点master,掌控全局视野,能让系统的复杂性降低非常多;

(2) master挂掉的概率很小;

(3) 不做高可用,能让系统的复杂性降低非常多;

 

worker的高可用是如何保证的?

master会周期性的ping每个worker,如果超时未返回,master会把对应的worker置为无效,把这个worker的工作任务重新执行:

1. 如果重新执行的是reduce任务,不需要有额外的通知;

2. 如果重新执行的是map任务,需要通知执行reduce的worker节点,输入数据换了一个worker;

 

随时都可能有map或者reduce挂掉,任务完成前重新被执行,会不会影响MR的最终结果?

在用户输入不变的情况下,MR的输出一定是不变的,这就要求MR系统必须具备幂等性:

1. 对相同的输入,不管哪个负责map的worker执行的结果,一定是不变的,产出的R个本地输出文件内容也一定是不变的;

2. 对于M个map,每个map输出的R个本地文件,只要这些输入不变,对应接收这些数据的reduce的worker执行结果,一定是不变的,输出文件内容也一定是不变的;

 

长尾效应怎么解决?

一个MR执行时间的最大短板,往往是“长尾worker”。

 

导致“长尾worker”的原因有很多:

(1) 用户的分区函数设计得不合理,导致某些reduce负载不均,要处理大量的数据;

画外音:

最坏的情况,所有数据最终都落到一个reduce上,分布式并行处理,转变为了单机串行处理;

所以,分区函数的负载均衡性,是用户需要考虑的。

 

(2) 因为系统的原因,worker所在的机器磁盘坏了,CPU有问题,也可能导致任务执行很慢;

GoogleMR有一个“备用worker”的机制,当某些worker的执行时间超出预期时,会启动另一个worker执行相同的任务,以尝试解决长尾效应。

 

总结

Google MapReduce架构,体现了很多经典架构实践:

1. 单点master简化系统复杂度;

2. 单点master不高可用,简化系统复杂度;

3. master对worker的监控以及重启,保证worker高可用;

4. 幂等性,保证结果的正确性;

5. 多个worker执行同一个任务优化长尾问题;

参考:《GFS架构启示(第84讲)
 
知其然,知其所以然。
思路比结论更重要。

 

==全文完==
MapReduce经典设计,给了我们哪些架构启示?(第85讲,超长文)
架构师之路:流量从10万到10亿,一定会遇到的80个架构问题(8000字长文)

想成为一个合格的架构师,经历系统流量从10万到10亿,一定会面临以下九大场景,80个架构问题。

画外音:

(1)文章较长,建议收藏;

(2)文章底部有视频版本;

 

【第一章:技术选型】

创业初期架构方案怎么选型?

(1)要考虑业务的需求与特点,初期往往“快速实现”更重要,此时系统的特点是请求量小,数据量小,服务器资源也非常有限;

(2)这个阶段最重要的选型依据是:合伙人熟悉什么技术栈,使用什么技术栈

(3)第一版往往采用ALL in one架构

(4)这个阶段研发主要在写CURD业务逻辑,引入DAO和ORM能极大提高工程效率;

画外音:什么是ALL in one架构?

 

如果硬要问我,会选择什么技术栈,我会二选一:

PHP体系(Linux,Apache,MySQL,PHP)

或者

Java体系(Linux,Tomcat,MySQL,Java)

 

使用开源框架组件还是自研?

我的观点是:

(1)早期不建议自研

(2)随着规模的扩大,要控制技术栈

(3)要浅浅的封装一层

(4)适当的时候,造一些契合业务的轮子;

画外音:为什么要控制技术栈?为什么要封装一层?

 

什么情况下要进行容量评估?

至少在三种情况下,要进行容量评估:

(1)新系统上线

(2)临时运营活动

(3)系统容量有质变性增长

 

系统层面,要评估哪些重要指标?

主要评估网络带宽、CPU、内存容量、磁盘容量、磁盘IO等资源指标,系统层面主要看吞吐量指标。

画外音:容量设计五大步骤是啥?

 

创业初期,系统层面存在瓶颈的时候,优化原则是什么?

(1)最低成本,初期最大的成本是时间成本;

(2)用“钱”和“资源”快速解决系统问题,而不是过早的系统重构;

(3)将ALL in one架构升级为伪分布式架构,是此阶段的最佳实践;

 

伪分布式的核心是什么?

伪分布式的本质是单机变多机,但又不是真正的高可用,其核心是垂直拆分:

(1)业务垂直拆分;

(2)代码垂直拆分;

(3)数据库垂直拆分;

(4)研发团队垂直拆分;

画外音:伪分布式的优化细节是啥?

 

【第二章:接入层架构】

如何解决接入层的扩展性问题?

引入反向代理。

 

最常见的反向代理是什么?

Nginx。

 

引入反向代理之后,要解决什么新的问题?

(1)集群负载均衡;

(2)反向代理高可用;

画外音:有哪些常见的负载均衡方法?如何保证反向代理高可用?

 

站点流量从小到大,接入层架构如何演进?

整体可以分为五个阶段:

(1)有反向代理技术之前,单体架构要解决扩展性问题,可使用DNS轮询架构;

(2)有反向代理技术之后,初期可以使用反向代理解决扩展性问题;

(3)然后,需要升级为高可用反向代理架构;

(4)多级反向代理,引入LVS&F5进一步扩充性能;

(5)想要无限性能,必须用DNS轮询架构;

画外音:每个阶段的逻辑与细节到底是怎么样的?

 

Session,是接入层架构非常关注的问题,如何保证Session一致性?

通常有四种方案:

(1)客户端层解决;

(2)反向代理层解决;

(3)web-server层解决;

(4)后端服务层解决

画外音:每种方案细节又是怎么样的?

 

CDN,是接入层不得不谈的问题,CDN架构有哪些要了解?

引入CDN架构,至少要考虑这五个问题:

(1)什么样的资源适合静态加速;

(2)CDN的架构是怎么样的;

(3)CDN是怎么实现“就近访问的”;

(4)如何保证源站和镜像站数据的一致性;

(5)资源更新,是推还是拉?

画外音:学CDN,千万不要去百度“斯塔尔报告”。

 

TCP接入,架构上要考虑哪些问题?

至少要考虑这四个架构设计点:

(1)TCP如何快速实现接入;

(2)TCP如何快速实现扩展,以及高可用;

(3)TCP如何快速实现负载均衡;

(4)TCP如何保证扩展性与耦合性的平衡;

画外音:有没有综合方案,系统性解决负载均衡 + 高可用 + 可扩展 + 解耦合等一系列问题?

【第三章:急速性能优化】
在互联网公司发展早期,为了产品快速迭代,最常使用的架构是什么?
ALL in one架构。
如果此时业务发展很快,系统成了瓶颈,架构优化的方向是什么?
用最短时间,以对代码最小的冲击,极速扩充系统性能。
早期如何快速的扩充系统性能?
使用三大分离的性能优化方法。
早期系统容易“白屏”,如何快速的提升用户体验,消除白屏?
动静分离。
什么是动静分离?
动静分离,是“静态页面与动态页面,分开不同的系统访问”的架构设计方法。
画外音:如何来实施?分别对应怎样的技术点?
如果静态页面访问这么快,动态页面访问这么慢,能否将“原本需要动态生成的页面,提前生成静态页面”?
可以,这是“页面静态化”技术,能够100倍提升访问速度。
画外音:这个技术适用怎么样的业务场景?
早期系统的主要瓶颈,最容易出现在哪里?
数据库读性能扛不住。
如何快速提升数据库读性能?
读写分离,使用数据库分组架构,一主多从,主从同步,读写分离。
画外音:读写分离,水平切分都是使用数据库集群,有什么异同?
后台运营系统,复杂的SQL语句对数据库性能影响较大,怎么办?
前台与后台分离。
画外音:前后端分离,前台后台分离,是一回事么?如何快速实施前台与后台分离?
【第四章:微服务架构】
系统初期,哪类技术栈最为流行?
(1)PHP语言的LAMP栈;;
(2)Java语言的LiToMyJa栈;
业务快速发展,三层架构可能存在哪些问题?
(1)代码频繁拷贝;
(2)底层复杂性扩散;
(3)公共库耦合;
(4)SQL质量不可控,数据库性能急剧下降;
(5)数据库耦合,无法实现增加实例扩容;
可以通过什么架构方案解决上述1-5问题?
微服务架构。
如果要落地微服务架构,服务粒度可以如何选择?
常见的有以下四种粒度:
(1)统一服务层;
(2)按业务划分服务;
(3)按库划分服务;
(4)按接口划分服务(需要轻量级进程等语言层面支持);
微服务架构,可能带来什么问题?
可能带来的潜在问题有:
(1)系统复杂性上升;
(2)层次间依赖关系变得复杂;
(3)运维,部署更麻烦;
(4)监控变得更复杂;
(5)定位问题更麻烦;
不要以为,引入一个RPC框架就是“微服务架构”了,微服务架构要解决很多问题。
微服务架构要解决哪些问题?
至少要解决高可用,无限性能扩展,负载均衡等众多架构基础问题。
如何解决高可用的问题?
每一层解决高可用问题的方案不一样,涉及虚IP,反向代理,集群,连接池,数据库分组,缓存冗余,故障转移等诸多技术。
画外音:高可用的方法论是什么?
如何解决无限性能扩展的问题?
每一层解决高可用问题的方案不一样,涉及Scale up,Scale out,DNS轮询,反向代理,连接池,水平切分等诸多技术。
画外音:无限性能的方法论是什么?
如何解决负载均衡的问题?
负载均衡分为两类:
(1)同构均匀分摊;
(2)异构按能力分摊;
异构服务器负载均衡,常见的这么几种方案:
(1)静态权重法;
(2)动态权重法,涉及“保险丝”算法;
同时,动态权重法还可以实现服务器的过载保护。
画外音:什么是“保险丝”算法?什么是过载保护?
哪一个组件,和高可用,无限性能扩展,负载均衡相关?
连接池。
连接池的核心是什么?
两个核心数据结构:连接数组,锁数据;
三个核心接口:初始化,拿出连接,放回连接;
画外音:如何快速掌握连接池内核?
【第五章:数据库架构】
工程上,数据库要设计一些什么?
(1)根据“业务模式”设计表结构;
(2)根据“访问模式”设计索引结构;
架构上,数据库还必须考虑什么?
(1)读性能提升;
(2)高可用;
(3)一致性保障;
(4)扩展性;
(5)垂直拆分;
画外音:我C,我居然从来没考虑过这些问题。
提升系统读取速度,有哪几种常见方法?
(1)建立索引;
(2)增加从库;
(3)增加缓存;
一个数据库分组集群,主从同步,读写分离,能不能在主库和从库建立不同的索引?
可以,例如:
(1)主库只响应写请求,不建立索引;
(2)线上从库,建立线上访问索引;
(3)后台从库,建立后台访问索引;
如何保证数据库的高可用?
核心思想是:冗余+故障自动转移:
(1)写库高可用,冗余写库;
(2)读库高可用,冗余读库;
数据冗余会带来什么副作用?
会引发一致性问题:
(1)两个写库数据可能不一致;
(2)主库和从库数据可能不一致;
写库高可用,两个写库相互同步数据,自增ID可能冲突导致数据不一致,有什么优化方案?
(1)为每个写库指定不同的初始值,相同的增长步长;
(2)生成不同的ID;
(3)一个写库提供服务,一个写库作为高可用影子主;
主从延时,有什么优化方案?
(1)业务容忍;
(2)强制读主;
(3)在从库有可能读到旧数据时,选择性读主(非常帅气的方案);
底层表结构变更,水平扩展分库个数发生变化,底层存储引擎升级,数据库如何平滑过度?
如果业务能够接受,可以停服扩展。
否则,可以使用以下三种方法:
(1)追日志平滑扩容法,平滑过度;
(2)双写平滑扩容法,平滑过度;
(3)秒级平滑扩容法(非常帅气的方案);
画外音:如何在秒级,实现读写实例加倍?容量加倍?
数据库垂直拆分的最佳实践是什么?
数据库垂直拆分,尽量把:
(1)长度短;
(2)访问频率高;
(3)经常一起访问;
的数据放在主表里。
【第六章:缓存架构】
工程上,缓存一般有几种使用方式?
(1)进程内缓存;
(2)进程外缓存,也就是缓存服务;
如果有多个服务使用进程内缓存,如何保证一致性?
常见的有三种方法:
(1)服务节点同步通知;
(2)MQ异步通知;
(3)牺牲少量一致性,定期后端更新;
绝大部分情况,还是应该使用缓存服务,缓存的使用,有什么注意点?
以下几点,应该要注意:
(1)服务与服务之间不要通过缓存传递数据;
(2)如果缓存挂掉,可能导致雪崩,此时要做高可用缓存,或者水平切分;
(3)调用方不宜再单独使用缓存存储服务底层的数据,容易出现数据不一致,以及反向依赖;
(4)不同服务,缓存实例要做垂直拆分,不宜共用缓存;
互联网缓存操作细节,最佳实践是什么?
Cache Aside Pattern。
Cache Aside Pattern的细节是什么?
它分为读缓存最佳实践,以及写缓存最佳实践。
读缓存最佳实践是:先读缓存,命中则返回;未命中则读数据库,然后设置缓存。
写缓存最佳实践是:
(1)淘汰缓存,而不是修改缓存;
(2)先操作数据库,再操作缓存;
画外音:为什么呢?
缓存的本质是“冗余了数据库中的数据”,可能存在什么问题?
缓存与数据库数据不一致。
什么场景下容易出现不一致?
写后立即读业务场景。
画外音:为什么呢?
出现不一致时,优化思路是什么?
及时把缓存中的脏数据淘汰掉。
具体要怎么淘汰,保证缓存与数据库中数据的一致性呢?
(1)服务同步二次淘汰法;
(2)服务异步二次淘汰法;
(3)线下异步二次淘汰法;
画外音:二次淘汰法,是很常见的一种实践。
目前缓存服务最常用的是什么?
Redis和memcache。
什么时候选择使用Redis?
以下场景优先使用Redis:
(1)需要支持复杂数据结构;
(2)需要支持持久化;
(3)需要天然高可用;
(4)value存储内容比较大;
如果只是纯KV,可以使用memcache。
画外音:纯KV场景,为什么memcache会更快呢?
【第七章:架构解耦】
配置文件,是互联网架构中不可缺少的一环。依赖(调用)某个下游服务集群,将下游集群信息放在自身配置文件里是一种惯用做法,该做法可能导致什么问题?
可能导致上下游严重耦合:
(1)上游痛:扩容的是下游,改配置重启的是上游;
画外音:架构设计中典型的反向依赖。
(2)下游痛:不知道谁依赖于自己,难以实施服务治理,按调用方限流;
为了解决上述问题,常见的配置架构演进是怎么样的?
(1)“配置私藏”架构;
(2)“全局配置文件”架构;
(3)“配置中心”架构;
除了配置中心,消息总线MQ也是互联网架构中的常见解耦利器。
一般来说,什么时候不使用MQ?
上游实时关注执行结果时,通常不使用MQ,而使用RPC调用。
什么情况下,可以使用MQ来解耦?
以下四种情况,可以使用MQ来解耦:
(1)数据驱动的任务依赖;
(2)上游不关心执行结果;
(3)上游关注结果,但执行时间很长,例如跨公网调用第三方服务;
(4)削峰填谷,流量控制,保护下游;
上下游IP耦合,可以如何解耦?
使用内网域名来代替内网IP。
多个模块,因为公共库而耦合在一起,可以如何解耦?
粗暴方案:代码各自拷贝一份(不太推荐)。
优化方案:
(1)垂直拆分,个性业务代码“上浮”;
(2)服务化,共性业务代码“下沉”;
多个模块,因为数据库而耦合在一起,可以如何解耦?
数据库耦合,需要对架构进行调整:
(1)第一步,公共数据访问服务化,数据私藏;
(2)第二步,个性数据访问,自己家的数据自己管理;
画外音:数据库耦合,当数据库成为瓶颈时,增加数据库实例也难以拆分扩容,非常头疼。
微服务拆分不完全,导致耦合,可以如何解耦?
(1)底层微服务功能要尽量通用;
(2)杜绝底层switch case不同业务类型的业务逻辑代码;
(3)个性化代码上浮,公共代码下沉,是更古不变的架构解耦准则;
画外音:架构复杂时,微服务是好事,但拆分不彻底反而会有坑。
【第八章:架构分层】
为什么互联网系统架构分层会越来越多?
当系统越来越复杂的时候,为了:
(1)让上游更高效的获取与处理数据,必须复用;
(2)让下游能屏蔽数据的获取细节,必须封装;
上述复用和封装,在系统架构上的呈现,就是“分层”
每次都要连接数据库获取数据,编码非常低效,有什么痛点?
每次数据库访问,都需要:
(1)创建连接,创建资源;
(2)拼装SQL;
(3)执行SQL并获取结果集;
(4)通过游标遍历结果集,拿到每一行,分析行数据,拿到每一列;
(5)关闭连接,回收资源;
很多重复的代码要编写,效率很低。
怎么优化?
分层抽象,分离DAO层。
单体架构,编码非常低效,有什么痛点?
每次数据获取,都需要:
(1)关注缓存细节(Redis?MC?);
(2)关注存储引擎细节(MySQL?);
(3)关注分库分表;
(4)关注数据路由规则;
很多重复的代码要编写,效率很低。
怎么优化?
分层抽象,分离基础服务层(微服务)。
微服务架构,编码非常低效,有什么痛点?
每次数据获取,都需要:
(1)要访问很多RPC接口,获取很多公共的数据;
(2)站点层与基础服务层之间的连接关系非常复杂;
(3)不同垂直业务之间有需要共性业务,代码要冗余很多次;
很多重复的代码要编写,效率很低。
怎么优化?
分层抽象,分离共性业务服务层。
画外音:可以先简单理解为“中台业务服务”。
PC/H5/APP多端业务,编码非常低效,有什么痛点?
每次数据获取,都需要:
(1)大部分业务逻辑相同,只有少量展现/交互不一样;
(2)一旦一个服务RPC接口升级,多端都要升级;
(3)一旦一个服务bug出现,多端都要升级;
(4)PC/H5/APP多端相同的逻辑存在大量代码拷贝;
很多重复的代码要编写,效率很低。
怎么优化?
分层抽象,前后端分离。
大数据量,高并发量的微服务架构,编码非常低效,有什么痛点?
数据库侧数据获取,往往:
(1)根据业务进行数据路由,水平切分;
(2)不确定路由的接口,要遍历全库;
(3)有时候要多个库拿数据,然后到内存排序,例如跨库分页需求;
(4)…
很多微服务有很多重复的代码要编写,效率很低。
怎么优化?
分层抽象,数据库中间件。
可以看到,架构分层,对研发效率的提升,与复杂性的屏蔽,至关重要。
【第九章:架构进阶】
负载均衡、数据收集、服务发现、调用链跟踪。这些非业务的功能,一般是谁实现的呢?
(1)互联网公司一般会有一个“架构部”,研发框架、组件、工具与技术平台;
(2)业务研发部门直接使用相关框架、组件、工具与技术平台,享受各种“黑科技”带来的便利;
对于上述“黑科技”的使用与推广,存在什么问题?
框架、组件、工具与技术平台的使用与推广,往往会遇到以下一些问题:
(1)业务研发团队,需要花大量时间去学习、使用基础框架与各类工具;
(2)架构部,对于“黑科技”不同语言客户端的支持,往往要开发C-client,Python-client,go-client,Java-client多语言版本;
(3)架构部,“黑科技” client要维护m个版本, server要维护n个版本,兼容性要测试m*n个版本;
(4)每次“黑科技”的升级,都需要推动上下游进行升级,这个周期往往是以季度、半年、又甚至更久,整体效率极低;
画外音:每次fastjson漏洞升级,要1个月。
如何来进行优化?
一个思路是,解耦,将业务服务拆分成两个进程:
图片
(1)一个进程实现业务逻辑(不管是调用方,还是服务提供方),biz,即上图白色方块;
(2)一个进程实现底层技术体系,proxy,即上图蓝色方块;
画外音:负载均衡、监控告警、服务发现与治理、调用链…等诸多基础设施,都放到这一层实现。
他们之间有这样一些特点:
图片
(1)biz和proxy共同诞生,共同消亡,互为本地部署,即上图虚线方框;
(2)biz和proxy之间,为本地通讯,即上图黑色箭头;
(3)所有biz之间的通讯,都通过proxy之间完成,proxy之间才存在远端连接,即上图红色箭头;
这样就实现了“业务的归业务,技术的归技术”,实现了充分解耦,如果所有节点都实现了解耦,整个架构会演变为:
图片
(1)绿色为biz;
(2)蓝色为proxy;
整个服务集群变成了网格状,这就是Service Mesh服务网格的由来。
Service Mesh的行业开源最佳实践是什么?
Istio。
Istio的架构核心是什么?
Istio架构分为两层:
(1)数据平面(data plane);
(2)控制平面(control plane);
图片
其架构核心方法论是:控制与实施分离。
画外音:具体envoy,mixer,citadel,pilot和galley的职责与细节,见《大专栏》。
前面所有章节讲的都是单机房架构,单机房架构的特点是什么?
架构分层之间,是全连接。
理想化的多机房架构,特点是什么?
架构分层之间,是同连接,即:站点,服务,数据全部单元化,仅连接同机房。
理想化的多机房架构,存在什么问题?
(1)并非所有的业务都能“单元化”;
(2)如果不能“单元化”,跨机房的数据同步存在较大延时;
有什么折衷方案?
可以实施“折衷多机房架构”。
什么是“折衷多机房架构”?
站点,服务,数据做不到全量单元化,做不到“只”连接同机房,但可以“最小化”跨机房连接,整个架构,可以只有两个地方跨机房:
(1)数据库写库(相比读,写的比例较小);
(2)数据库一处主从同步(本来就有延时);
折衷多机房架构,有什么优点?
机房区分主次,落地性强,对原有架构冲击较小,业务几乎不需要进行单元化改造。
画外音:更多多机房架构细节,详见《大专栏》。
18次直播回看,以及《架构师训练营》,系统性的详聊了上面这80个问题,感兴趣的同学可以扫码看细节。
18次直播精华回看,有哪些内容?
(1)每秒100w请求,秒杀架构
(2)千万粉丝,feed架构
(3)千万同时在线,IM架构
(4)每秒100w检索,搜索引擎内核架构
(5)MQ内核细节
(6)RPC内核细节
(7)数据库架构
(8)多机房多活架构与细节
(9)分布式调用链追踪架构与细节
(10)3周自研自动化上线平台
(11)区块链中的架构理念
(12)数据库性能瓶颈定位
(13)反范式数据库设计
(14)微服务抽离与解耦
(15)经典架构10问
(16)微服务与数据库架构10问
(17)技术人职业发展规划
(18)InnoDB内核架构与细节
每次1-2小时不等,全部已放出。
50节架构师训练营干货重放,有哪些内容?
第一阶:技术选型(5节,已放出)
第二阶:接入层架构(5节,已放出)
第三阶:极速性能优化(3节,已放出)
第四阶:微服务架构(7节,已放出)
第五阶:数据库架构(6节,已放出)
第六阶:缓存架构(7节,已放出)
第七阶:架构解耦(6节,已放出)
第八阶:架构分层(5节,已放出)
第九阶:架构进阶(6节,已放出)
把控住这些,应该能成为一名P8的架构师吧?
posted @ 2025-08-14 15:48  CharyGao  阅读(21)  评论(0)    收藏  举报