mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2025-10-20 08:53:50 +08:00
mod
This commit is contained in:
177
极客时间专栏/从0开始学大数据/模块三 大数据开发实践/18 | 如何自己开发一个大数据SQL引擎?.md
Normal file
177
极客时间专栏/从0开始学大数据/模块三 大数据开发实践/18 | 如何自己开发一个大数据SQL引擎?.md
Normal file
@@ -0,0 +1,177 @@
|
||||
<audio id="audio" title="18 | 如何自己开发一个大数据SQL引擎?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/9d/65/9d9a6a0590ddc5d22013efc967eb1165.mp3"></audio>
|
||||
|
||||
从今天开始我们就进入了专栏的第三个模块,一起来看看大数据开发实践过程中的门道。学习一样技术,如果只是作为学习者,被动接受总是困难的。**但如果从开发者的视角看,很多东西就豁然开朗了,明白了原理,有时甚至不需要学习,顺着原理就可以推导出各种实现细节**。
|
||||
|
||||
各种知识从表象上看,总是杂乱无章的,如果只是学习这些繁杂的知识点,固然自己的知识面是有限的,并且遇到问题的应变能力也很难提高。所以有些高手看起来似乎无所不知,不论谈论起什么技术,都能头头是道,其实并不是他们学习、掌握了所有技术,而是他们是在谈到这个问题的时候,才开始进行推导,并迅速得出结论。
|
||||
|
||||
我在Intel的时候,面试过一个交大的实习生,她大概只学过一点MapReduce的基本知识,我问她如何用MapReduce实现数据库的join操作,可以明显看出她没学习过这部分知识。她说:我想一下,然后盯着桌子看了两三秒的时间,就开始回答,基本跟Hive的实现机制一样。从她的回答就能看出这个女生就是一个高手,高手不一定要很资深、经验丰富,把握住了技术的核心本质,掌握了快速分析推导的能力,能够迅速将自己的知识技能推进到陌生的领域,就是高手。
|
||||
|
||||
这也是我这个专栏的目的,讲述大数据技术的核心原理,分享一些高效的思考和思维方式,帮助你构建起自己的技术知识体系。
|
||||
|
||||
在这个模块里,我将以大数据开发者的视角,讲述大数据开发需要关注的各种问题,以及相应的解决方案。希望你可以进入我的角色,跳出纷繁复杂的知识表象,掌握核心原理和思维方式,进而融会贯通各种技术,再通过各种实践训练,最终成为真正的高手。
|
||||
|
||||
前面专栏我们提到过,程序员的三大梦想,也就是开发数据库、操作系统和编译器。今天我们通过一个支持标准SQL语法的大数据仓库引擎的设计开发案例,看看如何自己开发一个大数据SQL引擎。
|
||||
|
||||
在学习今天的内容前,我们先回顾一下前面讨论过的大数据仓库Hive。作为一个成功的大数据仓库,它将SQL语句转换成MapReduce执行过程,并把大数据应用的门槛下降到普通数据分析师和工程师就可以很快上手的地步。这部分内容如果你忘记了,可以返回[专栏第11期](http://time.geekbang.org/column/article/69459)复习一下。
|
||||
|
||||
但是Hive也有自己的问题,由于它使用自己定义的Hive QL语法,这对已经熟悉Oracle等传统数据仓库的分析师来说,还是有一定的上手难度。特别是很多企业使用传统数据仓库进行数据分析已经由来已久,沉淀了大量的SQL语句,并且这些SQL经过多年的修改打磨,非常庞大也非常复杂。我曾经见过某银行的一条统计报表SQL,打印出来足足两张A4纸。这样的SQL光是完全理解可能就要花很长时间,再转化成Hive QL就更加费力,还不说转化修改过程中可能引入的bug。
|
||||
|
||||
2012年那会,我还在Intel亚太研发中心大数据团队,当时团队决定开发一款能够支持标准数据库SQL的大数据仓库引擎,希望让那些在Oracle上运行良好的SQL可以直接运行在Hadoop上,而不需要重写成Hive QL。这就是后来的[Panthera](http://github.com/intel-hadoop/project-panthera-ase)项目。
|
||||
|
||||
在开发Panthera前,我们分析一下Hive的主要处理过程,大体上分成三步:
|
||||
|
||||
1.将输入的Hive QL经过语法解析器转换成Hive抽象语法树(Hive AST)。<br>
|
||||
2.将Hive AST经过语义分析器转换成MapReduce执行计划。<br>
|
||||
3.将生成的MapReduce执行计划和Hive执行函数代码提交到Hadoop上执行。
|
||||
|
||||
Panthera的设计思路是保留Hive语义分析器不动,替换Hive语法解析器,使其将标准SQL语句转换成Hive语义分析器能够处理的Hive抽象语法树。用图形来表示的话,是用红框内的部分代替黑框内原来Hive的部分。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/72/b0/7236e0335857fd75d7d773634eaf51b0.png" alt="">
|
||||
|
||||
红框内的组件我们重新开发过,浅蓝色的是我们使用的一个开源的SQL语法解析器,将标准SQL解析成标准SQL抽象语法树(SQL AST),后面深蓝色的就是团队自己开发的SQL抽象语法树分析与转换器,将SQL AST转换成Hive AST。
|
||||
|
||||
那么标准SQL和Hive QL的差别在哪里呢?
|
||||
|
||||
标准SQL和Hive QL的差别主要有两个方面,一个是语法表达方式,Hive QL语法和标准SQL语法略有不同;另一个是Hive QL支持的语法元素比标准SQL要少很多,比如,数据仓库领域主要的测试集[TPC-H](http://www.tpc.org/tpch/)所有的SQL语句Hive都不支持。尤其是Hive不支持复杂的嵌套子查询,而对于数据仓库分析而言,嵌套子查询几乎是无处不在的。比如下面这样的SQL,在where查询条件existes里面包含了另一条SQL语句。
|
||||
|
||||
```
|
||||
select o_orderpriority, count(*) as order_count
|
||||
from orders
|
||||
where o_orderdate >= date '[DATE]'
|
||||
and o_orderdate < date '[DATE]' + interval '3' month
|
||||
and exists
|
||||
( select * from lineitem
|
||||
where l_orderkey = o_orderkey and l_commitdate < l_receiptdate )
|
||||
group by o_orderpriority order by o_orderpriority;
|
||||
|
||||
```
|
||||
|
||||
所以开发支持标准SQL语法的SQL引擎的难点,就变成**如何将复杂的嵌套子查询消除掉**,也就是where条件里不包含select。
|
||||
|
||||
SQL的理论基础是关系代数,而关系代数的主要操作只有5种,分别是并、差、积、选择、投影。所有的SQL语句最后都能用这5种操作组合完成。而一个嵌套子查询可以等价转换成一个连接(join)操作。
|
||||
|
||||
比如这条SQL
|
||||
|
||||
```
|
||||
select s_grade from staff where s_city not in (select p_city from proj where s_empname=p_pname)
|
||||
|
||||
```
|
||||
|
||||
这是一个在where条件里嵌套了not in子查询的SQL语句,它可以用left outer join和left semi join进行等价转换,示例如下,这是Panthera自动转换完成得到的等价SQL。这条SQL语句不再包含嵌套子查询,
|
||||
|
||||
```
|
||||
select panthera_10.panthera_1 as s_grade from (select panthera_1, panthera_4, panthera_6, s_empname, s_city from (select s_grade as panthera_1, s_city as panthera_4, s_empname as panthera_6, s_empname as s_empname, s_city as s_city from staff) panthera_14 left outer join (select panthera_16.panthera_7 as panthera_7, panthera_16.panthera_8 as panthera_8, panthera_16.panthera_9 as panthera_9, panthera_16.panthera_12 as panthera_12, panthera_16.panthera_13 as panthera_13 from (select panthera_0.panthera_1 as panthera_7, panthera_0.panthera_4 as panthera_8, panthera_0.panthera_6 as panthera_9, panthera_0.s_empname as panthera_12, panthera_0.s_city as panthera_13 from (select s_grade as panthera_1, s_city as panthera_4, s_empname as panthera_6, s_empname, s_city from staff) panthera_0 left semi join (select p_city as panthera_3, p_pname as panthera_5 from proj) panthera_2 on (panthera_0.panthera_4 = panthera_2.panthera_3) and (panthera_0.panthera_6 = panthera_2.panthera_5) where true) panthera_16 group by panthera_16.panthera_7, panthera_16.panthera_8, panthera_16.panthera_9, panthera_16.panthera_12, panthera_16.panthera_13) panthera_15 on ((((panthera_14.panthera_1 <=> panthera_15.panthera_7) and (panthera_14.panthera_4 <=> panthera_15.panthera_8)) and (panthera_14.panthera_6 <=> panthera_15.panthera_9)) and (panthera_14.s_empname <=> panthera_15.panthera_12)) and (panthera_14.s_city <=> panthera_15.panthera_13) where ((((panthera_15.panthera_7 is null) and (panthera_15.panthera_8 is null)) and (panthera_15.panthera_9 is null)) and (panthera_15.panthera_12 is null)) and (panthera_15.panthera_13 is null)) panthera_10 ;
|
||||
|
||||
```
|
||||
|
||||
通过可视化工具将上面两条SQL的语法树展示出来,是这样的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/2e/2b/2ebce9da37fff01e5701342699b69f2b.png" alt="">
|
||||
|
||||
这是原始的SQL抽象语法树。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/3a/15/3aafd8cf731eb6fd5b0e125847084f15.png" alt="">
|
||||
|
||||
这是等价转换后的抽象语法树,内容太多被压缩得无法看清,不过你可以感受一下(笑)。
|
||||
|
||||
那么,在程序设计上如何实现这样复杂的语法转换呢?当时Panthera项目组合使用了几种经典的设计模式,每个语法点被封装到一个类里去处理,每个类通常不过几十行代码,这样整个程序非常简单、清爽。如果在测试过程中遇到不支持的语法点,只需为这个语法点新增加一个类即可,团队协作与代码维护非常容易。
|
||||
|
||||
使用装饰模式的语法等价转换类的构造,Panthera每增加一种新的语法转换能力,只需要开发一个新的Transformer类,然后添加到下面的构造函数代码里即可。
|
||||
|
||||
```
|
||||
private static SqlASTTransformer tf =
|
||||
new RedundantSelectGroupItemTransformer(
|
||||
new DistinctTransformer(
|
||||
new GroupElementNormalizeTransformer(
|
||||
new PrepareQueryInfoTransformer(
|
||||
new OrderByTransformer(
|
||||
new OrderByFunctionTransformer(
|
||||
new MinusIntersectTransformer(
|
||||
new PrepareQueryInfoTransformer(
|
||||
new UnionTransformer(
|
||||
new Leftsemi2LeftJoinTransformer(
|
||||
new CountAsteriskPositionTransformer(
|
||||
new FilterInwardTransformer(
|
||||
//use leftJoin method to handle not exists for correlated
|
||||
new CrossJoinTransformer(
|
||||
new PrepareQueryInfoTransformer(
|
||||
new SubQUnnestTransformer(
|
||||
new PrepareFilterBlockTransformer(
|
||||
new PrepareQueryInfoTransformer(
|
||||
new TopLevelUnionTransformer(
|
||||
new FilterBlockAdjustTransformer(
|
||||
new PrepareFilterBlockTransformer(
|
||||
new ExpandAsteriskTransformer(
|
||||
new PrepareQueryInfoTransformer(
|
||||
new CrossJoinTransformer(
|
||||
new PrepareQueryInfoTransformer(
|
||||
new ConditionStructTransformer(
|
||||
new MultipleTableSelectTransformer(
|
||||
new WhereConditionOptimizationTransformer(
|
||||
new PrepareQueryInfoTransformer(
|
||||
new InTransformer(
|
||||
new TopLevelUnionTransformer(
|
||||
new MinusIntersectTransformer(
|
||||
new NaturalJoinTransformer(
|
||||
new OrderByNotInSelectListTransformer(
|
||||
new RowNumTransformer(
|
||||
new BetweenTransformer(
|
||||
new UsingTransformer(
|
||||
new SchemaDotTableTransformer(
|
||||
new NothingTransformer())))))))))))))))))))))))))))))))))))));
|
||||
|
||||
```
|
||||
|
||||
而在具体的Transformer类中,则使用组合模式对抽象语法树AST进行遍历,以下为Between语法节点的遍历。我们看到使用组合模式进行树的遍历不需要用递归算法,因为递归的特性已经隐藏在树的结构里面了。
|
||||
|
||||
```
|
||||
@Override
|
||||
protected void transform(CommonTree tree, TranslateContext context) throws SqlXlateException {
|
||||
tf.transformAST(tree, context);
|
||||
trans(tree, context);
|
||||
}
|
||||
|
||||
void trans(CommonTree tree, TranslateContext context) {
|
||||
// deep firstly
|
||||
for (int i = 0; i < tree.getChildCount(); i++) {
|
||||
trans((CommonTree) (tree.getChild(i)), context);
|
||||
}
|
||||
if (tree.getType() == PantheraExpParser.SQL92_RESERVED_BETWEEN) {
|
||||
transBetween(false, tree, context);
|
||||
}
|
||||
if (tree.getType() == PantheraExpParser.NOT_BETWEEN) {
|
||||
transBetween(true, tree, context);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
将等价转换后的抽象语法树AST再进一步转换成Hive格式的抽象语法树,就可以交给Hive的语义分析器去处理了,从而也就实现了对标准SQL的支持。
|
||||
|
||||
当时Facebook为了证明Hive对数据仓库的支持,Facebook的工程师手工将TPC-H的测试SQL转换成Hive QL,我们将这些手工Hive QL和Panthera进行对比测试,两者性能各有所长,总体上不相上下,这说明Panthera自动进行语法分析和转换的效率还是不错的。
|
||||
|
||||
Panthera(ASE)和Facebook手工Hive QL对比测试结果如下。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/c1/6f/c1866cede6eb563481d917b0f06b176f.png" alt="">
|
||||
|
||||
事实上,标准SQL语法集的语法点非常多,我们经过近两年的努力,绞尽脑汁进行各种关系代数等价变形,依然没有全部适配所有的标准SQL语法。
|
||||
|
||||
我在开发Panthera的时候,查阅了很多关于SQL和数据库的网站和文档,发现在我们耳熟能详的那些主流数据库之外还有很多名不见经传的数据库,此外,还有大量的关于SQL的语法解析器、测试数据和脚本集、各种周边工具。我们经常看到的MySQL、Oracle这些产品仅仅是整个数据库生态体系的冰山一角。还有很多优秀的数据库在竞争中落了下风、默默无闻,而更多的支撑起这些优秀数据库的论文、工具,非业内人士几乎闻所未闻。
|
||||
|
||||
这个认识给了我很大触动,我一直期待我们中国人能开发出自己的操作系统、数据库、编程语言,也看到有很多人前仆后继投入其中。但是这么多年过去了,大部分努力惨淡收场,小部分结果沦落为笑柄,成为人们饭后的谈资。我曾经思考过,为什么会这样?
|
||||
|
||||
开发Panthera之后,我想,我们国家虽然从事软件开发的人数很多,但是绝大多数都在做最顶层的应用开发,底层技术开发和研究的人太少,做出来的成果也太少。我们在一个缺乏周边生态体系、没有足够的竞争产品的情况下,就想直接开发出自己有影响力的操作系统、数据库、编程语言,无异于想在沙漠里种出几棵参天大树。
|
||||
|
||||
不过,庆幸的是,我也看到越来越多有全球影响力的底层技术产品中出现中国人的身影,小草已经在默默生长,假以时日,料想必有大树出现。
|
||||
|
||||
今天我讲的是一个SQL引擎是如何设计出来的,也许在你的工作几乎不可能去开发SQL引擎,但是了解这些基础的知识,了解一些设计的技巧,对你用好数据库,开发更加灵活、有弹性的系统也会很有帮助。
|
||||
|
||||
## 思考题
|
||||
|
||||
SQL注入是一种常见的Web攻击手段,如下图所示,攻击者在HTTP请求中注入恶意SQL命令(drop table users;),服务器用请求参数构造数据库SQL命令时,恶意SQL被一起构造,并在数据库中执行。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/8c/4f/8c92076fc8b4005ce075734e9641cd4f.png" alt="">
|
||||
|
||||
但是JDBC的PrepareStatement可以阻止SQL注入攻击,MyBatis之类的ORM框架也可以阻止SQL注入,请从数据库引擎的工作机制解释PrepareStatement和MyBatis的防注入攻击的原理。
|
||||
|
||||
欢迎你点击“请朋友读”,把今天的文章分享给好友。也欢迎你写下自己的思考或疑问,与我和其他同学一起讨论。
|
105
极客时间专栏/从0开始学大数据/模块三 大数据开发实践/19 | Spark的性能优化案例分析(上).md
Normal file
105
极客时间专栏/从0开始学大数据/模块三 大数据开发实践/19 | Spark的性能优化案例分析(上).md
Normal file
@@ -0,0 +1,105 @@
|
||||
<audio id="audio" title="19 | Spark的性能优化案例分析(上)" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/a6/9b/a63eaccdb5c467197624bf83ab20cf9b.mp3"></audio>
|
||||
|
||||
我们知道,现在最主流的大数据技术几乎都是开源的产品,不管是Hadoop这样的大数据存储与计算产品,还是Hive、Spark SQL这样的大数据仓库,又或者Storm、Flink这样的大数据流计算产品,还有Mahout、MLlib这样的大数据机器学习算法库,它们都来自开源社区。所以,我们在使用大数据、学习大数据的过程中肯定少不了要和开源社区打交道。
|
||||
|
||||
我在Intel工作期间主要工作就是参与Apache开源社区的大数据项目开发,其实上一期我讲的Panthera最初也是准备为Hive项目增强标准SQL处理能力而开发,但是因为和Apache Hive项目管理方在开发理念上的冲突,最终选择独立开源。后来我又参与了Apache Spark的开发,为Spark源代码提交了一些性能优化的Patch。我想通过专栏两期的内容,具体介绍一下如何参与Apache这样开源社区的软件开发,如何进行软件性能优化,以及我在Apache Spark源码上做的一些优化实践。
|
||||
|
||||
一方面我希望你能借此更深入、系统地了解软件性能优化;另一方面也可以更深入了解Spark的一些运行机制,同时也可以了解Apache开源社区的运作模式。因为我们在使用各类大数据产品的时候,一定会遇到各种问题,想要解决这些问题,你可以直接到官方的开源社区去求助并寻找答案。在使用过程中,如果这些大数据产品不能满足你的需求,你可以阅读源代码并直接对源代码进行修改和优化。因为你在实践过程中产生的需求可能其他人也会有,你可以将你修改的源代码提交到开源社区,请求合并到发布版本上,供全世界开发者使用。这也是开源最大的魅力。
|
||||
|
||||
你可能已经注意到,作为软件开发人员,日常我们使用的大量软件,基本上全部来自美国,不管是免费开源的Linux、Java、Hadoop、PHP、Tomcat、Spring,还是商业收费的Windows、WebLogic、Oracle,大到编程语言、操作系统、数据库,小到编程框架、日志组件,几乎全部来自美国。
|
||||
|
||||
软件,特别是开源软件,是没有国界的,属于全人类的技术财富。但是,我觉得我们还要承认,中美之间的技术差距真的很惊人。在当前这样一个中美贸易摩擦不断的背景下,难免让人有些忧虑。缩短这种技术差距也许非一日之功,但是更多的中国工程师参与到开源软件的开发中,让中国在世界软件技术领域获得很多影响力,也许是当下就可以迈出的一步。
|
||||
|
||||
## Apache开源社区的组织和参与方式
|
||||
|
||||
Apache是一个以基金会方式运作的非盈利开源软件组织,旗下有超过一百个各类开源软件,其中不乏Apache、Tomcat、Kafka等知名的开源软件,当然也包括Hadoop、Spark等最主流的大数据开源软件。
|
||||
|
||||
Apache每个项目的管理团队叫项目管理委员会(PMC),一般由项目发起者、核心开发者、Apache基金会指定的资深导师组成,主导整个项目的发展。此外,项目的主要开发者叫作committer,是指有将代码合并到主干代码权限的开发者,而其他没有代码合并权限的开发者叫作contributor。
|
||||
|
||||
一般说来,参与Apache开源产品开发,先从contributor做起。一般的流程是,从GitHub项目仓库fork代码到自己的仓库,在自己仓库修改代码然后创建pull request,提交到Spark仓库后,如果有committer认为没问题,就merge到Spark主干代码里。
|
||||
|
||||
一旦你为某个Apache项目提交的代码被merge到代码主干,你就可以宣称自己是这个项目的contributor了,甚至可以写入自己的简历。如果能持续提交高质量的代码,甚至直接负责某个模块,你就有可能被邀请成为committer,会拥有一个apache.org后缀的邮箱。
|
||||
|
||||
当然我希望你提交的是有质量的代码,而不仅仅是对代码注释里某个单词拼写错误进行修改,然后就号称自己是某个著名开源项目的contributor了。虽然修改注释也是有价值的,但是如果你的pull request总是修改注释的拼写错误,很难被认为是一个严肃的开发者。
|
||||
|
||||
除了Apache,Linux、以太坊等开源基金会的组织和运作方式也都类似。就我观察,最近几年,越来越多来自中国的开发者开始活跃在各种重要的开源软件社区里,我希望你也成为其中一员。
|
||||
|
||||
## 软件性能优化
|
||||
|
||||
在熟悉开源社区的运作方式后,接下来我们就可以考虑开始进行性能优化了。但在上手之前,你是否清楚所谓性能优化具体要做些什么呢?
|
||||
|
||||
关于软件性能优化,有个著名的**论断**。
|
||||
|
||||
1.你不能优化一个没有经过性能测试的软件。
|
||||
|
||||
2.你不能优化一个你不了解其架构设计的软件。
|
||||
|
||||
不知你是否听过这个论断,我来解释一下。
|
||||
|
||||
如果没有性能测试,那么你就不会知道当前软件的主要性能指标有哪些。通常来说,软件的主要性能指标包括:
|
||||
|
||||
<li>
|
||||
响应时间:完成一次任务(请求)花费的时间。
|
||||
</li>
|
||||
<li>
|
||||
并发数:同时处理的任务数(请求数)。
|
||||
</li>
|
||||
<li>
|
||||
吞吐量:单位时间完成的任务数(请求数、事务数、查询数……)。
|
||||
</li>
|
||||
<li>
|
||||
性能计数器:System Load,线程数,进程数,CPU、内存、磁盘、网络使用率等。
|
||||
</li>
|
||||
|
||||
如果没有性能指标,我们也就不清楚软件性能的瓶颈,优化前和优化后也是无从对比。这样的优化工作只能是主观臆断:别人这样做说性能好,我们也这样优化。
|
||||
|
||||
而如果不了解软件的架构设计,你可能根本无从判断性能瓶颈产生的根源,也不知道该从哪里优化。
|
||||
|
||||
所以性能优化的一般过程是:
|
||||
|
||||
1.做性能测试,分析性能状况和瓶颈点。
|
||||
|
||||
2.针对软件架构设计进行分析,寻找导致性能问题的原因。
|
||||
|
||||
3.修改相关代码和架构,进行性能优化。
|
||||
|
||||
4.做性能测试,对比是否提升性能,并寻找下一个性能瓶颈。
|
||||
|
||||
## 大数据软件性能优化
|
||||
|
||||
在大数据使用、开发过程的性能优化一般可以从以下角度着手进行。
|
||||
|
||||
**1. SQL语句优化**。使用关系数据库的时候,SQL优化是数据库优化的重要手段,因为实现同样功能但是不同的SQL写法可能带来的性能差距是数量级的。我们知道在大数据分析时,由于数据量规模巨大,所以SQL语句写法引起的性能差距就更加巨大。典型的就是Hive的MapJoin语法,如果join的一张表比较小,比如只有几MB,那么就可以用MapJoin进行连接,Hive会将这张小表当作Cache数据全部加载到所有的Map任务中,在Map阶段完成join操作,无需shuffle。
|
||||
|
||||
**2. 数据倾斜处理**。数据倾斜是指当两张表进行join的时候,其中一张表join的某个字段值对应的数据行数特别多,那么在shuffle的时候,这个字段值(Key)对应的所有记录都会被partition到同一个Reduce任务,导致这个任务长时间无法完成。淘宝的产品经理曾经讲过一个案例,他想把用户日志和用户表通过用户ID进行join,但是日志表有几亿条记录的用户ID是null,Hive把null当作一个字段值shuffle到同一个Reduce,结果这个Reduce跑了两天也没跑完,SQL当然也执行不完。像这种情况的数据倾斜,因为null字段没有意义,所以可以在where条件里加一个userID != null过滤掉就可以了。
|
||||
|
||||
**3. MapReduce、Spark代码优化**。了解MapReduce和Spark的工作原理,了解要处理的数据的特点,了解要计算的目标,设计合理的代码处理逻辑,使用良好的编程方法开发大数据应用,是大数据应用性能优化的重要手段,也是大数据开发工程师的重要职责。
|
||||
|
||||
**4. 配置参数优化**。根据公司数据特点,为部署的大数据产品以及运行的作业选择合适的配置参数,是公司大数据平台性能优化最主要的手段,也是大数据运维工程师的主要职责。比如Yarn的每个Container包含的CPU个数和内存数目、HDFS数据块的大小和复制数等,每个大数据产品都有很多配置参数,这些参数会对大数据运行时的性能产生重要影响。
|
||||
|
||||
**5. 大数据开源软件代码优化**。曾经和杭州某个SaaS公司的大数据工程师聊天,他们的大数据团队只有5、6个人,但是在使用开源大数据产品的时候,遇到问题都是直接修改Hadoop、Spark、Sqoop这些产品的代码。修改源代码进行性能优化的方法虽然比较激进,但是对于掌控自己公司的大数据平台来说,效果可能是最好的。
|
||||
|
||||
## Spark性能优化
|
||||
|
||||
有了上面这些性能优化原则和过程,我们在了解Spark架构和代码的基础上,就可以进行性能优化了。
|
||||
|
||||
关于性能测试,我们使用的是Intel为某视频网站编写的一个基于Spark的关系图谱计算程序,用于计算视频的级联关系。我们使用5台服务器对样例数据进行性能测试,程序运行总体性能如下图。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/2b/d0/2bf9e431bbd543165588a111513567d0.png" alt="">
|
||||
|
||||
这张图我在专栏Spark架构原理分析过。我们将4台Worker服务器上主要计算资源利用率指标和这张图各个job与stage的时间点结合,就可以看到不同运行阶段的性能指标如何,从而发现性能瓶颈。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7a/cc/7af885b0492aa68ffbe05bee7e04cdcc.png" alt=""><br>
|
||||
<img src="https://static001.geekbang.org/resource/image/2f/12/2f8f43d795247f575e953a027070d012.png" alt=""><br>
|
||||
<img src="https://static001.geekbang.org/resource/image/86/56/86cb0d0beea4178cbf5f7031fec7a956.png" alt=""><br>
|
||||
<img src="https://static001.geekbang.org/resource/image/09/33/093c888a13a58802413f9d1e2eafcb33.png" alt="">
|
||||
|
||||
从这些图我们可以看到,CPU、内存、网络、磁盘这四种主要计算资源的使用和Spark的计算阶段密切相关。后面我主要通过这些图来分析Spark的性能问题,进而寻找问题根源,并进一步进行性能优化。
|
||||
|
||||
下一期,我们一起来看几个Spark性能优化的案例,进一步了解Spark的工作原理以及性能优化的具体实践。
|
||||
|
||||
## 思考题
|
||||
|
||||
如果性能测试发现,网卡是整个系统的瓶颈,程序运行过程中网卡达到了最大I/O能力,整个系统经常在等待网卡的数据传输,请问,你有什么性能优化建议呢?
|
||||
|
||||
欢迎你点击“请朋友读”,把今天的文章分享给好友。也欢迎你写下自己的思考或疑问,与我和其他同学一起讨论。
|
149
极客时间专栏/从0开始学大数据/模块三 大数据开发实践/20 | Spark的性能优化案例分析(下).md
Normal file
149
极客时间专栏/从0开始学大数据/模块三 大数据开发实践/20 | Spark的性能优化案例分析(下).md
Normal file
@@ -0,0 +1,149 @@
|
||||
<audio id="audio" title="20 | Spark的性能优化案例分析(下)" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/fe/6b/fe6d2b587455604761ef6a1f6963bc6b.mp3"></audio>
|
||||
|
||||
上一期,我讲了软件性能优化必须经过进行性能测试,并在了解软件架构和技术的基础上进行。今天,我们通过几个Spark性能优化的案例,看一看所讲的性能优化原则如何落地。如果你忘记了性能优化的原则,可以返回上一期复习一下。
|
||||
|
||||
基于软件性能优化原则和Spark的特点,Spark性能优化可以分解为下面几步。
|
||||
|
||||
1.性能测试,观察Spark性能特性和资源(CPU、Memory、Disk、Net)利用情况。
|
||||
|
||||
2.分析、寻找资源瓶颈。
|
||||
|
||||
3.分析系统架构、代码,发现资源利用关键所在,思考优化策略。
|
||||
|
||||
4.代码、架构、基础设施调优,优化、平衡资源利用。
|
||||
|
||||
5.性能测试,观察系统性能特性,是否达到优化目的,以及寻找下一个瓶颈点。
|
||||
|
||||
下面我们一起进入详细的案例分析,希望通过这几个案例,可以帮助你更好地理解Spark的原理,以及性能优化如何实践落地,希望能对你有所启发。
|
||||
|
||||
## 案例1:Spark任务文件初始化调优
|
||||
|
||||
首先进行性能测试,发现这个视频图谱N度级联关系应用分为5个job,最后一个job为保存结果到HDFS,其余job为同样计算过程的反复迭代。但是发现第一个job比其他job又多了个计算阶段stage,如图中红圈所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/6f/88/6fd436e3c6c11106cd7754792e78ee88.png" alt="">
|
||||
|
||||
通过阅读程序代码,发现第一个job需要初始化一个空数组,从而产生了一个stage,但是这个stage在性能测试结果上显示,花费了14秒的时间,远远超出合理的预期范围。同时,发现这段时间网络通信也有一定开销,事实上只是内存数据初始化,代码上看不出需要进行网络通信的地方。下图是其中一台计算节点的通信开销,发现在第一个stage,写通信操作几乎没有,读通信操作大约每秒几十MB的传输速率。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/05/58/054abfc46ca040d3db8c441822a86558.png" alt="">
|
||||
|
||||
分析Spark运行日志,发现这个stage主要花费时间并不是处理应用的计算逻辑,而是在从Driver进程下载应用执行代码。前面说过,Spark和MapReduce都是通过移动计算程序到数据所在的服务器节点,从而节省数据传输的网络通信开销,并进行分布式计算,即移动计算比移动数据更划算,而移动计算程序就是在这个阶段进行。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f8/18/f8828271641cb854a0e1e79b75ab8218.png" alt="">
|
||||
|
||||
这个视频关系图谱计算程序因为依赖一个第三方的程序包,整个计算程序打包后大小超过17MB,这个17MB的JAR包需要部署到所有计算服务器上,即Worker节点上。但是只传输17MB的数据不可能花费这么多时间啊?
|
||||
|
||||
进一步分析Spark日志和代码后发现,每个计算节点上会启动多个Executor进程进行计算,而Spark的策略是每个Executor进程自己去下载应用程序JAR包,当时每台机器启动了30个Executor进程,这样就是4×30=120个进程下载,而Driver进程所在机器是一块千兆网卡,导致将这些数据传输完成花费了14秒的时间。
|
||||
|
||||
发现问题以后,解决办法就显而易见了。同一台服务器上的多个Executor进程不必每个都通过网络下载应用程序,只需要一个进程下载到本地后,其他进程将这个文件copy到自己的工作路径就可以了。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/82/f0/823fb2e270b7031542edeafd088d06f0.jpg" alt="">
|
||||
|
||||
这段代码有个技术实现细节需要关注,就是多个进程同时去下载程序包的时候,如何保证只有一个进程去下载,而其他进程阻塞等待,也就是进程间的同步问题。
|
||||
|
||||
解决办法是使用了一个本地文件作为进程间同步的锁,只有获得文件锁的进程才去下载,其他进程得不到文件锁,就阻塞等待,阻塞结束后,检查本地程序文件是否已经生成。
|
||||
|
||||
这个优化实测效果良好,第一个stage从14秒下降到不足1秒,效果显著。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ea/32/ea59ae5b5e70304ac6f1fcf1bacd3332.png" alt="">
|
||||
|
||||
这个案例的具体代码你可以参考:<br>
|
||||
[https://github.com/apache/spark/pull/1616](https://github.com/apache/spark/pull/1616)
|
||||
|
||||
## 案例2:Spark任务调度优化
|
||||
|
||||
继续前面的性能测试,看看有没有新的性能瓶颈以及性能指标不合理的地方。我们将4台Worker机器的CPU使用率进行对比分析,发现CPU使用率有些蹊跷的地方。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/49/38/498e4d3d7aa0c23b6fc5807eb87b7638.png" alt=""><br>
|
||||
<img src="https://static001.geekbang.org/resource/image/6e/64/6eb9b6f7ff05a9d521035898f830d964.png" alt=""><br>
|
||||
<img src="https://static001.geekbang.org/resource/image/b6/00/b6e5868d6af8ffbd3ddc99a0ad9e4b00.png" alt=""><br>
|
||||
<img src="https://static001.geekbang.org/resource/image/f8/5b/f82efd934ed0e1992cb7bb9460b9175b.png" alt="">
|
||||
|
||||
从图中看到,在第一个job的第二个阶段,第三台机器的CPU使用率和其他机器明显不同,也就是说计算资源利用不均衡,**这种有忙有闲的资源分配方式通常会引起性能问题**。
|
||||
|
||||
分析Spark运行日志和Spark源代码,发现当有空闲计算资源的Worker节点向Driver注册的时候,就会触发Spark的任务分配,分配的时候使用轮询方式,每个Worker都会轮流分配任务,保证任务分配均衡,每个服务器都能领到一部分任务。但是为什么实测的结果却是在第二个stage,只有一个Worker服务器领了任务,而其他服务器没有任何任务可以执行?
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/4c/63/4c69cb903334886ec12dc98b4af8b563.png" alt="">
|
||||
|
||||
进一步分析日志,发现Worker节点向Driver注册有先有后,先注册的Worker开始领取任务,如果需要执行的任务数小于Worker提供的计算单元数,就会出现一个Worker领走所有任务的情况。
|
||||
|
||||
而第一个job的第二个stage刚好是这样的情况,demo数据量不大,按照HDFS默认的Block大小,只有17个Block,第二个stage就是加载这17个Block进行初始迭代计算,只需要17个计算任务就能完成,所以当第三台服务器先于其他三台服务器向Driver注册的时候,触发Driver的任务分配,领走了所有17个任务。
|
||||
|
||||
同时,为了避免这种一个Worker先注册先领走全部任务的情况,我们考虑的一个优化策略是增加一个配置项,只有注册的计算资源数达到一定比例才开始分配任务,默认值是0.8。
|
||||
|
||||
```
|
||||
spark.scheduler.minRegisteredResourcesRatio = 0.8
|
||||
|
||||
```
|
||||
|
||||
为了避免注册计算资源达不到期望资源比例而无法开始分配任务,在启动任务执行时,又增加了一个配置项,也就是最小等待时间,超过最小等待时间(秒),不管是否达到注册比例,都开始分配任务。
|
||||
|
||||
```
|
||||
spark.scheduler.maxRegisteredResourcesWaitingTime = 3
|
||||
|
||||
```
|
||||
|
||||
启用这两个配置项后,第二个stage的任务被均匀分配到4个Worker服务器上,执行时间缩短了1.32倍。而4台Worker服务器的CPU利用率也变得很均衡了。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/80/c5/804e83b440d287343c49febe58b8c5c5.png" alt=""><br>
|
||||
<img src="https://static001.geekbang.org/resource/image/2a/c1/2a005427e41e5e78bb15d08c39f057c1.png" alt=""><br>
|
||||
<img src="https://static001.geekbang.org/resource/image/0c/4a/0ce1eab6a0d7800c80d8312b23cb854a.png" alt=""><br>
|
||||
<img src="https://static001.geekbang.org/resource/image/b5/70/b5557db2bcfad01dc9d6e5506d77ea70.png" alt="">
|
||||
|
||||
这个案例的具体代码你可以参考:[https://github.com/apache/spark/pull/900](https://github.com/apache/spark/pull/900)<br>
|
||||
[https://github.com/apache/spark/pull/1525](https://github.com/apache/spark/pull/1525)
|
||||
|
||||
## 案例3:Spark应用配置优化
|
||||
|
||||
看案例2的几张CPU利用率的图,我们还发现所有4个Worker服务器的CPU利用率最大只能达到60%多一点。例如下图,绿色部分就是CPU空闲。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/b5/70/b5557db2bcfad01dc9d6e5506d77ea70.png" alt="">
|
||||
|
||||
这种资源利用瓶颈的分析无需分析Spark日志和源代码,根据Spark的工作原理,稍加思考就可以发现,当时使用的这些服务器的CPU的核心数是48核,而应用配置的最大Executor数目是120,每台服务器30个任务,虽然30个任务在每个CPU核上都100%运行,但是总的CPU使用率仍只有60%多。
|
||||
|
||||
具体优化也很简单,设置应用启动参数的Executor数为48×4=192即可。
|
||||
|
||||
## 案例4:操作系统配置优化
|
||||
|
||||
在性能测试过程中发现,当使用不同服务器的时候,CPU资源利用情况也不同,某些服务器的CPU处于sys态,即系统态运行的占比非常高,如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/2e/c7/2e95c4c900c6afb7918e9efa6341bac7.png" alt="">
|
||||
|
||||
图中紫色为CPU处于sys态,某些时候sys态占了CPU总使用率的近80%,这个比例显然是不合理的,表示虽然CPU很忙,但是没有执行用户计算,而是在执行操作系统的计算。
|
||||
|
||||
那么,操作系统究竟在忙什么,占用了这么多CPU时间?通过跟踪Linux内核执行指令,发现这些sys态的执行指令和Linux的配置参数transparent huge pages有关。
|
||||
|
||||
当transparent huge pages打开的时候,sys态CPU消耗就会增加,而不同Linux版本的transparent huge pages默认是否打开是不同的,对于默认打开transparent huge pages的Linux执行下面的指令,关闭transparent huge pages。
|
||||
|
||||
```
|
||||
echo never > /sys/kernel/mm/transparent_hugepage/enabled
|
||||
echo never > /sys/kernel/mm/ transparent_hugepage/defrag
|
||||
|
||||
```
|
||||
|
||||
关闭以后,对比前面的CPU消耗,sys占比明显下降,总的应用耗时也有明显下降。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/24/94/24b6e43e406ac4eae3f44a94ddd2fb94.png" alt="">
|
||||
|
||||
## 案例5:硬件优化
|
||||
|
||||
分析网卡的资源消耗,发现网络通信是性能的瓶颈,对整个应用的影响非常明显。比如在第二个、第三个job,网络通信消耗长达50秒的时间,网络读写通信都达到了网卡的最大吞吐能力,整个集群都在等待网络传输。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7d/84/7ddc70b5388ebc8a0b05959cbbf6f384.png" alt="">
|
||||
|
||||
我们知道千兆网卡的最大传输速率是每秒125MB,这样的速率和CPU内存固然没法比,而虽然比单个磁盘快一些,但是服务器磁盘是8块磁盘组成的阵列,总的磁盘吞吐量依然碾压千兆网卡,因此网卡传输速率的瓶颈就成为整个系统的性能瓶颈。
|
||||
|
||||
而优化手段其实很简单粗暴,就是升级网卡使用万兆网卡。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/44/0e/4495bc09fde62c856c2ff4316092c20e.png" alt="">
|
||||
|
||||
硬件优化的效果非常明显,以前需要50多秒的网络通信时间,缩短为10秒左右。从性能曲线上看,网络通信在刚刚触及网卡最大传输速率的时候,就完成了传输,总的计算时间缩短了近100秒。
|
||||
|
||||
## 小结
|
||||
|
||||
一般说来,大数据软件性能优化会涉及硬件、操作系统、大数据产品及其配置、应用程序开发和部署几个方面。当性能不能满足需求的时候,先看看各项性能指标是否合理,如果资源没有全面利用,那么可能是配置不合理或者大数据应用程序(包括SQL语句)需要优化;如果某项资源利用已经达到极限,那么就要具体来分析,是集群资源不足,需要增加新的硬件服务器,还是需要对某项硬件、操作系统或是JVM,甚至是对大数据产品源代码进行调优。
|
||||
|
||||
## 思考题
|
||||
|
||||
关于目前的主要大数据产品,你在学习、使用过程中,从SQL写法、应用编程、参数配置,到大数据产品自身的架构原理与源码实现,你有没有发现有哪些可以进行性能优化的地方?
|
||||
|
||||
欢迎你点击“请朋友读”,把今天的文章分享给好友。也欢迎你写下自己的思考或疑问,与我和其他同学一起讨论。
|
@@ -0,0 +1,69 @@
|
||||
<audio id="audio" title="21 | 从阿里内部产品看海量数据处理系统的设计(上):Doris的立项" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/ca/78/cab1834b7604435e435cdf128244d278.mp3"></audio>
|
||||
|
||||
从今天开始,我会分两期内容来讨论阿里巴巴的一个海量数据处理系统的设计,这个系统的名字叫Doris,它是阿里巴巴的一个内部产品。前面专栏曾经提到过,2010年前后是各种NoSQL系统爆发的一个时期,各种开源NoSQL在这个时期发布出来,当时阿里巴巴也开发了自己的NoSQL系统Doris。
|
||||
|
||||
Doris的设计目标是支持海量的KV结构的数据存储,访问速度和可靠性要高于当时主流的NoSQL数据库,系统要易于维护和伸缩。和当时众多NoSQL系统相比,Doris在架构设计上颇具独特性,路由算法、失效转移、集群扩容也有自己的创新之处,并成功申请三项技术专利。
|
||||
|
||||
**在我们开始讨论Doris项目前,我想先跟你聊聊大公司是如何看待内部技术产品这件事**。事实上,阿里巴巴内部底层技术产品的研发决策思路也颇有值得借鉴之处,你可以吸收其中好的经验,并把它转化到你所开发的产品上。
|
||||
|
||||
我们知道一家互联网公司主要靠自己的互联网产品盈利,比如阿里巴巴主要靠淘宝、天猫、阿里巴巴B2B网站等产品赚钱,而公司的工程师主要也是开发这些产品,但是这些产品通常都需要处理海量的用户请求和大规模的数据存储,所以在系统底层通常用到很多基础技术产品,比如分布式缓存、分布式消息队列、分布式服务框架、分布式数据库等。这些基础技术产品可以选择开源技术产品,也可以选择自己研发。自己研发的优点是可以针对业务场景进行定制开发,同时培养提高自己工程师的技术实力;缺点是投入大、风险高。
|
||||
|
||||
通常公司到了一定规模,都会开始逐渐自主研发一些基础技术产品,既可以提升自己的产品研发能力,又可以提高自身在业界的地位,吸引更优秀的人才并提高竞争门槛,形成自己的竞争壁垒。
|
||||
|
||||
但是公司的资源毕竟是有限的,主要的资源又投入到业务产品开发去了,那剩下的资源到底应该投入到哪里呢?这需要形成公司内部一套竞争策略,以使优秀的项目能够得到资源。
|
||||
|
||||
另一方面,对工程师而言,业务产品的开发技术难度相对较低,如果要想更快提高自己的技术水平,去开发基础技术产品更能得到提升和锻炼,所以优秀的工程师更愿意去开发有难度有挑战的创新性基础技术产品,而不是去开发那些千篇一律的业务产品。
|
||||
|
||||
这样,在工程师和公司之间就形成了一种博弈:工程师想要开发基础技术产品,但是必须要得到公司管理层的支持;管理层资源有限,只愿意支持开发那些对业务有价值、技术有创新、风险比较低的基础技术产品。
|
||||
|
||||
所以事情就变成工程师需要说服公司管理层,想要做的就是对业务有价值、技术有创新、风险比较低的基础技术产品;而管理层则要从这些竞争者中选出最优秀的项目。
|
||||
|
||||
通过这种博弈,公司的资源会凝聚到最有价值的技术产品上,优秀的工程师也会被吸引到这些项目上,最后实现了公司价值和员工价值的统一和双赢。
|
||||
|
||||
下面我们进入正题,我会拿出当时Doris开发立项时说服管理层用的PPT,向你解读个中技巧以及Doris的创新设计。 需要提醒你的是,你在学习这两期专栏时可以试着想象一个场景,假设是在Doris项目的立项启动会,今天你是老板,看看你最关注一个项目的哪些技术指标;又或者你是Doris项目的工程师,可以想想哪些指标是老板关注的,并且从技术上是可以实现的。这样把自己带入到一个角色中,对于你更好理解这个数据处理系统很有帮助。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/60/05/604b240c9adaff2d84e9603d96e54905.png" alt=""><br>
|
||||
<img src="https://static001.geekbang.org/resource/image/05/b2/055f0ba8cfd561e306c7345e6c0cc7b2.png" alt="">
|
||||
|
||||
PPT开篇就是当前现状,当时阿里巴巴没有统一的大数据NoSQL解决方案,有的产品是自己在业务代码中实现数据分区逻辑,从而实现海量KV数据的存储访问。这样做的主要问题有
|
||||
|
||||
<li>
|
||||
开发困难。程序员在开发时要知道自己存储的数据在哪台服务器。
|
||||
</li>
|
||||
<li>
|
||||
运维困难。增加服务器的时候,需要开发配合,故障的时候也很难排查问题。
|
||||
</li>
|
||||
|
||||
现状一定是有问题的,需要我们去解决。有没有现成的解决方案?有,但是现成的方案也有问题,所以我们必须要自己开发一套系统才能解决问题。这样,后面想做的一切才能顺理成章。
|
||||
|
||||
当你想做一个新东西,它必须要能解决当前的问题,这是人类社会的基本运行规律。如果当前没有问题呢?你相信我,这个世界不可能没有问题的,重要的是你要能发现问题。就像你做的东西将来也一定会有问题,因为现在的产品在将来一定会落伍,但那已经不再是你的问题。
|
||||
|
||||
**技术只是手段,技术不落在正确的问题上一点用也没有,而落在错误的问题上甚至会搬起石头砸了自己的脚**。而什么是正确的问题,你需要自己去思考和发现。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/29/fd/2968c50bd69d1d7574d4eec40207b8fd.png" alt="">
|
||||
|
||||
前一页说完了当前存在的问题,引出了我们必须要自己开发一个海量数据处理系统,这一页就要说明这个产品的定位,也就是“海量分布式透明KV存储引擎”,这个引擎能够实现的业务价值就是能够支撑阿里巴巴未来各个主要产品的海量数据存储访问需求。
|
||||
|
||||
这两页是整个PPT的灵魂,管理层如果对第一页提出的问题不认可,又对第二页产品要实现的价值不以为然,那基本上这个项目也就凉凉了。
|
||||
|
||||
如果到这里没有问题,得到认可,那下一步就要趁热打铁,**突出项目的创新和特点**。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a5/58/a50720fdba66389dd7d75d2871e75f58.png" alt="">
|
||||
|
||||
产品的功能目标和非功能目标要清晰、要有亮点,和业界主流产品比要有竞争优势(用红色字体标出),要更贴合公司的业务场景。Doris的主要功能目标是提供KV存储,非功能目标包括在运维上要实现集群易于管理,具有自我监控和自动化运维功能,不需要专业运维人员维护;要支持集群线性伸缩,平滑扩容;具有自动容错和故障转移的高可用性;高并发情况下快速响应的高性能性;支持未来功能持续升级的可扩展性。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/2c/43/2c86070b16950dd1e3976a9e20e42d43.png" alt="">
|
||||
|
||||
技术指标也要亮眼,至少不能明显低于当前主流同类产品的指标。当时Doris根据阿里巴巴的内部使用需求场景,支持所有的B2B业务的KV存储,因此设计目标是未来部署一个100~10000台服务器的集群规模,并不支持无限伸缩。如果前面说过别的产品的缺点,这里也要对应说明自己强在哪里。
|
||||
|
||||
设计指标的设定,既不能低,如果比目前主流同类产品的指标还要差,自己再开发这样的产品就没有意义;也不能太高,如果设定太高,过度承诺,让老板、用户对你未来交付的产品抱有太高的期望,将来稍有不慎,无法达到期望,不但对产品的发展造成不良影响,甚至大家对你的人品都会产生怀疑。做好对别人的期望管理,让大家对你既充满期待,又不至于不切实际,不但对你的职业发展大有帮助,应用到生活中也会获益良多。
|
||||
|
||||
到这里,问题也说了、方向也有了、设计指标也定了,究竟能不能开发出满足设计目标的产品,就看后面的PPT把核心架构和关键设计讲清楚,要证明自己有把握、有能力做到。
|
||||
|
||||
到底如何证明自己能做到,且听下回分解。
|
||||
|
||||
## 思考题
|
||||
|
||||
在你的工作环境中,哪些工作是更有技术挑战和难度的工作?现在是否有人在做?如果你想做,该如何说服上司支持你?
|
||||
|
||||
欢迎你点击“请朋友读”,把今天的文章分享给好友。也欢迎你写下自己的思考或疑问,与我和其他同学一起讨论。
|
120
极客时间专栏/从0开始学大数据/模块三 大数据开发实践/22 | 从阿里内部产品看海量数据处理系统的设计(下):架构与创新.md
Normal file
120
极客时间专栏/从0开始学大数据/模块三 大数据开发实践/22 | 从阿里内部产品看海量数据处理系统的设计(下):架构与创新.md
Normal file
@@ -0,0 +1,120 @@
|
||||
<audio id="audio" title="22 | 从阿里内部产品看海量数据处理系统的设计(下):架构与创新" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/f9/9c/f97451b08b0f9d3a2e8624e3c18a219c.mp3"></audio>
|
||||
|
||||
上一期,Doris提出了目前阿里巴巴海量KV存储方面的问题,给出了Doris的业务价值、设计目标和技术指标。但是Doris项目组还必须证明自己有已经经过论证的架构技术方案,可以实现前面设定的目标,立项后可以迅速启动执行,不需要再去摸索尝试,风险可以把控。
|
||||
|
||||
因此,PPT后面的内容主要就是阐述Doris的架构方案和创新设计。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/eb/5b/eb949889c4fe1823ced433ea59123a5b.png" alt="">
|
||||
|
||||
Doris是一种支持Key、Value数据结构的分布式存储系统,核心要解决的问题是分布式路由、分布式集群伸缩、分布式数据冗余与失效转移。所以Doris把分布式存储系统很重要的一块,也就是数据存储部分转移出去,使用第三方软件完成,当时选择Berkeley DB作为Doris的底层存储Store,Doris自己专注于分布式技术实现。
|
||||
|
||||
Doris的主要访问模型是,应用程序KV Client启动后,连接控制中心Administration,从控制中心获得整个Doris集群的服务器部署信息及路由算法,Client使用Key作为参数进行路由计算,计算得到集群中某些服务器作为当前Key、Value数据存储的服务器节点;然后KV Client使用自定义的通信协议将数据和命令传输给服务器上的Data Server组件,DataServer再调用本地的Berkeley DB将数据存储到本地磁盘。
|
||||
|
||||
Doris的核心技术就是这个架构模型上创新性地实现了自己独特的**分区路由算法、失效转移策略、集群伸缩设计方案**。并在项目开发过程中,将这个三个技术创新申请了技术专利。下面我们重点看下这三个技术创新。
|
||||
|
||||
## 分区路由算法
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/01/d6/019cd0d87daf9d356bd7326cd2a928d6.png" alt="">
|
||||
|
||||
Doris采用一种基于虚拟节点的分区路由算法,Key使用余数Hash算法计算得到虚拟节点下标。
|
||||
|
||||
```
|
||||
虚拟节点下标 = hash(md5(key)) mod 虚拟节点个数
|
||||
|
||||
```
|
||||
|
||||
虚拟节点和物理服务器节点之间计算建立一个映射关系,通过映射关系查找实际要访问的物理服务器IP地址。
|
||||
|
||||
路由算法在初始化的时候就预先设立一个较大的数字,比如100000,当存储服务器集群需要伸缩的时候,要增加一个服务器,虚拟节点和下标计算算法不变,仅仅调整虚拟节点和物理服务器节点的映射关系就可以了,如PPT中图2所示。
|
||||
|
||||
这种基于虚拟节点的分区路由算法相对于传统的一致性Hash路由算法,可以获得更好的数据负载均衡,即数据在各个服务器上的存储分布更加均衡。在集群伸缩、增加服务器的时候可以做到更少迁移数据。在实践中,这种算法的一个更大优势是,如果将物理存储的文件系统和虚拟节点关联,即一个虚拟节点对应一个物理存储文件,那么当集群扩容,进行数据迁移的时候,就可以以文件为单位进行数据拷贝,这样迁移速度和运维成本都非常低。
|
||||
|
||||
这个基于虚拟节点的分区路由算法的关键难点是,如何计算虚拟节点与物理节点的映射关系,特别是在增加服务器的时候,如何重新计算这个映射关系,使新的映射关系依然处于负载均衡的状态,也就是每个物理节点映射的虚拟节点个数差不太多相同。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/0a/b8/0a9dde28106a7b279048d755390638b8.png" alt="">
|
||||
|
||||
项目组抽象了一个数学公式完成映射关系的计算,你可以看上面PPT示例。
|
||||
|
||||
## 失效转移策略
|
||||
|
||||
在前面在技术指标上,曾经承诺Doris的可用性为99.997%,保证数据可用性的策略主要是数据存储冗余备份和数据访问失效转移。
|
||||
|
||||
我们先看下Doris如何实现冗余备份。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/cd/5e/cd2bd7fc384155e29a2b0dad4c92575e.png" alt="">
|
||||
|
||||
Doris将存储服务器集群分成多个group(默认情况下为2个group),数据写操作的时候,根据分区路由算法,在每个group里计算一个服务器地址,异步并发同时向多个group的服务器上写入数据,以此保证数据有多个备份。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/eb/fc/eba8dc671ec1a750fae74221a0782cfc.png" alt="">
|
||||
|
||||
当KV Client访问某台服务器失败的时候,Doris会启动失效转移策略。具体来说,Doris将失效分为三种情况:瞬时失效、临时失效、永久失效,不同情况采用不同的失效转移策略。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/05/0d/0584b85e62b7917fce2f11f587ea8e0d.png" alt="">
|
||||
|
||||
当第一次不能访问服务器的时候,Doris认为这是瞬时失效,会进行访问重试,如果三次重试后仍然失败,就会把失败信息提交给控制中心。控制中心检测该服务器心跳是否正常,并进行尝试访问,如果访问失败,就将该服务器标记为临时失效,并通知所有KV Client应用程序。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/8f/e8/8fb946ae9879bfbfdf403a8b686605e8.png" alt="">
|
||||
|
||||
KV Client应用程序收到服务器失效通知的时候,启动临时失效策略,将原本需要写入到失效节点(图中的物理节点2)的数据写入临时日志节点(图中的物理节点X),而读操作则只访问正常的物理节点1。
|
||||
|
||||
当临时失效节点2恢复正常运行,系统会将失效期间写入临时日志节点X的数据合并恢复到物理节点2,这段时间物理节点2只提供写服务,不提供读服务。当所有数据恢复完毕,集群访问恢复正常。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/39/87/390216499d8a68f9f9f607bd18fc8987.png" alt="">
|
||||
|
||||
而对于永久失效的节点,需要添加新的服务器以代替下线的服务器,基本策略就是将另一个group正常使用的服务器数据拷贝到新添加的服务器上即可。
|
||||
|
||||
需要说明的是,上述三种失效转移过程,除了服务器永久失效后,需要工程师手动添加服务器,并到控制中心添加新服务器配置、激活启用外,其他情况不需要任何人工干预,全部自动化完成。
|
||||
|
||||
## 集群伸缩设计
|
||||
|
||||
分布式系统的一个重要设计目标是集群弹性可伸缩,如果当前的服务器数目不能满足业务的负载压力要求,那么就添加更多的服务器去增强处理能力。对于分布式数据存储服务器的伸缩性扩容而言,必然伴随着数据的迁移,就是将原先服务器中的部分数据迁移到新的服务器上。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/dd/fe/ddaf1e4c4e13be3bb1085f2dda9ecefe.png" alt="">
|
||||
|
||||
具体过程为:
|
||||
|
||||
1.向集群中一个分组group添加新的物理服务器,部署并启动Doris服务器进程。
|
||||
|
||||
2.将这个group的所有服务器设置为临时失效。
|
||||
|
||||
3.使用路由算法重新计算加入服务器后的虚拟节点分布,并把需要迁移的虚拟节点对应的物理文件拷贝到新服务器上。
|
||||
|
||||
4.设置group所有服务器临时失效恢复,将扩容期间的数据更新写回到这些服务器。
|
||||
|
||||
至此,PPT最前面提出的设计目标,经过一系列的关键技术设计分析,证明是技术是可行的,风险是可控的,可以启动开发了。
|
||||
|
||||
实际上当时项目组大概花了半年的时间开发Doris系统,部署上线以后,阿里巴巴多个业务产品接入Doris,并在极少运维的情况下,无故障运行了数年。后来服务器集群经过几次扩容,规模达到数百台服务器,实践证明当时的设计是经得起考验的。
|
||||
|
||||
最后,我想再说一下关于专利的事。公司一般都是希望能够申请更多的技术专利,这样在跟其他公司进行专利大战的时候才能做到“手中有枪,心中不慌”,特别是在遇到“专利流氓”的时候。所以大部分公司对工程师申请技术专利都比较支持。
|
||||
|
||||
大一点的公司法务部门通常会有专门的知识产权律师,他们会帮助工程师申请技术专利,工程师只要按照一般写技术文档的写法写一个技术交底书给公司律师,律师审核后会让专门的专利代理公司帮助编写专门的技术专利申请书,所以工程师申请专利的工作量并不大。
|
||||
|
||||
很多公司为了支持申请技术专利,会有很多奖励,比如申请成功一个专利会有几万的奖励,这对于工程师也是一笔不错的收入。做技术的同学可以关注下自己公司的专利奖励政策,如果还没有相关的专利奖励,正好你也可以借此机会说服公司管理层在这方面增加一些激励,这是一件利国家、利公司、利自己的好事。
|
||||
|
||||
## 小结
|
||||
|
||||
分布式数据存储系统是分布式系统中最有技术挑战的领域之一。其他的各种分布式系统,由于对数据的一致性和系统的可用性要求并没有那么高 ,所以技术难度和挑战相对没有分布式存储系统这么高。自己参与设计、开发这样的系统,会对分布式系统,乃至大数据系统有更深刻地理解,希望这两期专栏能引导你从开发者的视角,看待分布式大数据系统是如何设计开发出来的,从而对大数据技术有新的认识和领悟。
|
||||
|
||||
如果你在工作中遇到有技术挑战的项目,可以尽量找机会去参与,你能收获的不仅仅是最终开发出来的产品和公司的认可,还有自己技术的提升和更有想象力的职业前景。
|
||||
|
||||
## 思考题
|
||||
|
||||
今天的文中提到,Doris的分区路由算法在设计的时候,提出了一个数学模型计算虚拟节点和物理节点的映射关系。但是最后在开发过程中,项目组并没有使用这个数学模型进行计算,你能想到的实现算法还有什么呢?
|
||||
|
||||
你也可以在Doris的源代码中找到相关代码,分析Doris的最终实现算法和你的思考有什么异同。
|
||||
|
||||
欢迎你点击“请朋友读”,把今天的文章分享给好友。也欢迎你写下自己的思考或疑问,与我和其他同学一起讨论。
|
||||
|
||||
扩展阅读:
|
||||
|
||||
对专利感兴趣的同学可以进一步阅读Doris产品申请的三个技术专利:
|
||||
|
||||
[http://www.soopat.com/Patent/201110325238](http://www.soopat.com/Patent/201110325238)
|
||||
|
||||
[http://www.soopat.com/Patent/201110294092](http://www.soopat.com/Patent/201110294092)
|
||||
|
||||
[http://www.soopat.com/Patent/201110285802](http://www.soopat.com/Patent/201110285802)
|
||||
|
||||
Doris源代码地址:
|
||||
|
||||
[https://github.com/itisaid/Doris](https://github.com/itisaid/Doris)
|
122
极客时间专栏/从0开始学大数据/模块三 大数据开发实践/23 | 大数据基准测试可以带来什么好处?.md
Normal file
122
极客时间专栏/从0开始学大数据/模块三 大数据开发实践/23 | 大数据基准测试可以带来什么好处?.md
Normal file
@@ -0,0 +1,122 @@
|
||||
<audio id="audio" title="23 | 大数据基准测试可以带来什么好处?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/5a/73/5a7f6c76c651ac09f88de68ab36ab673.mp3"></audio>
|
||||
|
||||
2012年的时候,Hadoop已经日趋成熟,Intel的大数据团队也正准备寻找新的技术研究方向。当时,我们对比测试了多个新出来的大数据技术产品,最终选择了Spark重点跟进参与。现在看来,这是一个明智的决定,作出这个决定是基于大数据基准测试,而使用的对比测试工具就是我今天要讲的大数据基准测试工具HiBench。
|
||||
|
||||
大数据作为一个生态体系,不但有各种直接进行大数据处理的平台和框架,比如HDFS、MapReduce、Spark,还有很多周边的支撑工具,而大数据基准测试工具就是其中一个大类。
|
||||
|
||||
## 大数据基准测试的应用
|
||||
|
||||
大数据基准测试的主要用途是对各种大数据产品进行测试,检验大数据产品在不同硬件平台、不同数据量、不同计算任务下的性能表现。
|
||||
|
||||
上面这样讲大数据基准测试的用途可能比较教条,我举两个例子你就能明白它的应用有多么重要了。
|
||||
|
||||
还是回到2012年,当时Hive只能做离线的SQL查询计算,无法满足数据分析师实时交互查询的需求,业界需要一款更快的ad hoc query(即席查询,一种非预设查询的SQL访问)工具。在这种情况下,Cloudera推出了准实时SQL查询工具Impala。Impala兼容Hive的Hive QL语法和Hive MetaSotre,也支持Hive存储在HDFS的数据表,但是放弃了Hive较慢的MapReduce执行引擎,而是基于MPP(Massively Parallel Processing,大规模并行处理)的架构思想重新开发了自己的执行引擎,从而获得更快的查询速度。
|
||||
|
||||
由于Cloudera在大数据领域的巨大权威,加上人们对快速SQL查询的期待,Impala在刚推出的时候,受到大数据业界的极大瞩目。当时,我也立即用四台服务器部署了一个小集群,利用大数据基准测试工具HiBench对Impala和Hive做了一个对比测试。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/96/9b/961e6cc96cb0beb649d96bd21ed62b9b.png" alt="">
|
||||
|
||||
但是经过对比测试以后,我发现情况并不乐观。Impala性能有优势的地方在于聚合查询,也就是用group by查询的SQL语句;而对于连接查询,也就是用join查询的SQL语句性能表现很差。我进一步阅读Impala的源代码,对设计原理和架构进行分析后,得出了自己的看法,我认为适合Impala的应用场景有两类:
|
||||
|
||||
<li>
|
||||
一类是简单统计查询,对单表数据进行聚合查询,查看数据分布规律。
|
||||
</li>
|
||||
<li>
|
||||
一类是预查询,在进行全量数据的SQL查询之前,对抽样数据进行快速交互查询,验证数据分析师对数据的判断,方便数据分析师后续设计全量数据的查询SQL,而全量数据的SQL还是要运行在Hive上。
|
||||
</li>
|
||||
|
||||
这样Impala就有点尴尬了,它的定位似乎只是Hive的附属品。这就好比Impala是餐前开胃菜和餐后甜点,而正餐依然是Hive。
|
||||
|
||||
但是Cloudera却对Impala寄予厚望,后来我和Cloudera的工程师聊天,得知他们投入了公司近一半的工程师到Impala的开发上,我还是有点担心。事实上,这么多年过去了,Impala经过不断迭代,相比最开始的性能有了很大改进,但是我想,Impala依然没有承担起Cloudera对它的厚望。
|
||||
|
||||
跟Impala相对应的是,同样是2012年,Intel大数据团队用大数据基准测试工具HiBench对Spark和MapReduce做了对比测试后发现,Spark运行性能有令人吃惊的表现。当时Intel大数据团队的负责人戴老师(Jason Dai)立即飞到美国,跟当时开发Spark的UC Berkeley的AMP实验室交流,表示Intel愿意参与到Spark的开发中。Spark也极其希望有业界巨头能够参与其中,开发代码尚在其次,重要的是有了Intel这样的巨头背书,Spark会进一步得到业界的认可和接受。
|
||||
|
||||
所以Intel成了Spark最早的参与者,加速了Spark的开发和发展。当2013年Spark加入Apache的开源计划,并迅速成为Apache的顶级项目,风靡全球的大数据圈子时,Intel作为早期参与者,也得到了业界的肯定,使Intel在大数据领域可以保持持续的影响力。
|
||||
|
||||
在这个案例里,所有各方都是赢家,Spark、Intel、Apache,乃至整个大数据行业,我作为Intel参与Spark早期开发的工程师,也都因此而受益。这也是我关于工作的一个观点:好的工作不光是对公司有利,对员工也是有利的。工作不是公司在压榨员工的过程,而是公司创造价值,同时员工实现自我价值的过程。
|
||||
|
||||
而如何才能创造出好的工作也不只是公司的责任,主要还是要靠员工自己,去发现哪些事情能够让自己、公司、社会都获益,然后去推动这些事情的落实,虽然有的时候推动比发现更困难。同时拥有发现和推动能力的人,毫无例外都是一些出类拔萃的人,比如专栏前面也提到的Intel的戴老师,这些人都是我工作的榜样。
|
||||
|
||||
## 大数据基准测试工具HiBench
|
||||
|
||||
大数据基准测试工具有很多,今天我重点为你介绍前面我多次提到的,也是Intel推出的大数据基准测试工具[HiBench](http://github.com/intel-hadoop/HiBench)。
|
||||
|
||||
HiBench内置了若干主要的大数据计算程序作为基准测试的负载(workload)。
|
||||
|
||||
<li>
|
||||
Sort,对数据进行排序大数据程序。
|
||||
</li>
|
||||
<li>
|
||||
WordCount,前面多次提到过,词频统计大数据计算程序。
|
||||
</li>
|
||||
<li>
|
||||
TeraSort,对1TB数据进行排序,最早是一项关于软件和硬件的计算力的竞赛,所以很多大数据平台和硬件厂商进行产品宣传的时候会用TeraSort成绩作为卖点。
|
||||
</li>
|
||||
<li>
|
||||
Bayes分类,机器学习分类算法,用于数据分类和预测。
|
||||
</li>
|
||||
<li>
|
||||
k-means聚类,对数据集合规律进行挖掘的算法。
|
||||
</li>
|
||||
<li>
|
||||
逻辑回归,数据进行预测和回归的算法。
|
||||
</li>
|
||||
<li>
|
||||
SQL,包括全表扫描、聚合操作(group by)、连接操作(join)几种典型查询SQL。
|
||||
</li>
|
||||
<li>
|
||||
PageRank,Web排序算法。
|
||||
</li>
|
||||
|
||||
此外还有十几种常用大数据计算程序,支持的大数据框架包括MapReduce、Spark、Storm等。
|
||||
|
||||
对于很多非大数据专业人士而言,HiBench的价值不在于对各种大数据系统进行基准测试,而是学习大数据、验证自己大数据平台性能的工具。
|
||||
|
||||
对于一个刚刚开始入门大数据的工程师而言,在自己的电脑上部署了一个伪分布式的大数据集群可能并不复杂,对着网上的教程,顺利的话不到1个小时就可以拥有自己的大数据集群。
|
||||
|
||||
但是,接下来呢?开发MapReduce程序、打包、部署、运行,可能这里每一步都会遇到很多挫折。即使一切顺利,但顾名思义对于“大数据”来说,需要大量的数据才有意义,那数据从哪儿来呢?如果想用一些更复杂的应用体验下大数据的威力,可能遇到的挫折就更多了,所以很多人在安装了Hadoop以后,然后就放弃了大数据。
|
||||
|
||||
对于做大数据平台的工程师,如果等到使用者来抱怨自己维护的大数据平台不稳定、性能差的时候,可能就有点晚了,因为这些消息可能已经传到老板那里了。所以必须自己不停地跑一些测试,了解大数据平台的状况。
|
||||
|
||||
有了HiBench,这些问题都很容易就可以解决,HiBench内置了主要的大数据程序,支持多种大数据产品。最重要的是使用特别简单,初学者可以把HiBench当作学习工具,可以很快运行起各种数据分析和机器学习大数据应用。大数据工程师也可以用HiBench测试自己的大数据平台,验证各种大数据产品的性能。
|
||||
|
||||
HiBench使用非常简单,只需要三步:
|
||||
|
||||
1.配置,配置要测试的数据量、大数据运行环境和路径信息等基本参数。
|
||||
|
||||
2.初始化数据,生成准备要计算的数据,比如要测试1TB数据的排序,那么就生成1TB数据。
|
||||
|
||||
3.执行测试,运行对应的大数据计算程序。
|
||||
|
||||
具体初始化和执行命令也非常简单,比如要生成数据,只需要运行bin目录下对应workload的prepare.sh就可以自动生成配置大小的数据。
|
||||
|
||||
```
|
||||
bin/workloads/micro/terasort/prepare/prepare.sh
|
||||
|
||||
```
|
||||
|
||||
要执行大数据计算,运行run.sh就可以了。
|
||||
|
||||
```
|
||||
bin/workloads/micro/terasort/hadoop/run.sh
|
||||
bin/workloads/micro/terasort/spark/run.sh
|
||||
|
||||
```
|
||||
|
||||
## 小结
|
||||
|
||||
同一类技术问题的解决方案绝不会只有一个,技术产品也不会只有一个,比如大数据领域,从Hadoop到Spark再到Flink,各种大数据产品层出不穷,那么如何对比测试这些大数据产品,在不同的应用场景中它们各自的优势是什么?这个时候就需要用到基准测试工具,通过基准测试工具,用最小的成本得到我们想测试的结果。
|
||||
|
||||
所以除了大数据,在很多技术领域都有基准测试,比如数据库、操作系统、计算机硬件等。前几年手机领域的竞争聚焦在配置和性能上,各路发烧友们比较手机优劣的时候,口头禅就是“跑个分试试”,这也是一种基准测试。
|
||||
|
||||
因此基准测试对这些产品而言至关重要,甚至攸关生死。得到业界普遍认可的基准测试工具就是衡量这些产品优劣的标准,如果能使基准测试对自己的产品有利,更是涉及巨大的商业利益。我在Intel开始做SQL引擎开发,后来做Spark开发,需要调查各种数据库和大数据的基准测试工具,也就是在那个时候,我发现华为这家公司还是很厉害的,在很多基准测试标准的制定者和开发者名单中,都能看到华为的名字,而且几乎是唯一的中国公司。
|
||||
|
||||
有时候我们想要了解一个大数据产品的性能和用法,看了各种资料花了很多时间,最后得到的可能还是一堆不靠谱的N手信息。但自己跑一个基准测试,也许就几分钟的事,再花点时间看看测试用例,从程序代码到运行脚本,很快就能了解其基本用法,更加省时、高效。
|
||||
|
||||
## 思考题
|
||||
|
||||
今天文章的Impala VS Hive的基准测试报告里,发现当数量很大的时候做join查询,Impala会失去响应,是因为Impala比Hive更消耗内存,当内存不足时,就会失去响应。你能否从Impala的架构和技术原理角度分析为什么Impala比Hive更消耗内存?
|
||||
|
||||
欢迎你点击“请朋友读”,把今天的文章分享给好友。也欢迎你写下自己的思考或疑问,与我和其他同学一起讨论。
|
||||
|
||||
|
117
极客时间专栏/从0开始学大数据/模块三 大数据开发实践/24 | 从大数据性能测试工具Dew看如何快速开发大数据系统.md
Normal file
117
极客时间专栏/从0开始学大数据/模块三 大数据开发实践/24 | 从大数据性能测试工具Dew看如何快速开发大数据系统.md
Normal file
@@ -0,0 +1,117 @@
|
||||
<audio id="audio" title="24 | 从大数据性能测试工具Dew看如何快速开发大数据系统" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/55/d9/5582d9ae22eab104f5be3466f13b61d9.mp3"></audio>
|
||||
|
||||
我们在[Spark性能优化案例分析](http://time.geekbang.org/column/article/72056)这一期中,通过对大量的Spark服务器的性能数据进行可视化分析,发现了Spark在程序代码和运行环境中的各种性能问题,并做了相应优化,使Spark运行效率得到了极大提升。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7a/cc/7af885b0492aa68ffbe05bee7e04cdcc.png" alt="">
|
||||
|
||||
很多同学也在问,这些可视化的性能数据从何而来呢?如何在图中将性能指标和任务进度结合起来,可以一目了然看清应用在不同运行阶段的资源使用状况呢?事实上,当时为了进行Spark性能优化,我和团队小伙伴们开发了一个专门的大数据性能测试工具[Dew](https://github.com/zhihuili/Dew)。
|
||||
|
||||
## Dew设计与开发
|
||||
|
||||
Dew自身也是一个分布式的大数据系统,部署在整个Hadoop大数据集群的所有服务器上。它可以实时采集服务器上的性能数据和作业日志,收集起来以后解析这些日志数据,将作业运行时间和采集性能指标的时间在同一个坐标系绘制出来,就得到上面的可视化性能图表。Dew的部署模型如下。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7f/81/7f0cf1566ba2f7fccd41b036112e7f81.png" alt="">
|
||||
|
||||
从图中看,Dew的核心进程有两种,一种是Dew Master进程Herse,另一种是管理集群中每台服务器的Dew Agent进程DewDrop,Dew Agent监控整个Hadoop集群的每台服务器。Herse独立部署一台服务器,而DewDrop则和HDFS的DataNode、Yarn的NodeManager部署在大数据集群的其他所有服务器上,也就是每台服务器都同时运行DataNode、NodeManager、DewDrop进程。
|
||||
|
||||
Dew Master服务器上配置好Agent服务器的IP,运行下面的命令就可以启动整个Dew集群。
|
||||
|
||||
```
|
||||
sbin/start-all.sh
|
||||
|
||||
```
|
||||
|
||||
Master进程Herse和每一台服务器上的Agent进程DewDrop都会启动起来,DewDrop进程会向Herse进程注册,获取自身需要执行的任务,根据任务指令,加载任务可执行代码,启动Drop进程内的service,或者独立进程service,即各种App。整个启动和注册时序请看下面这张图。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/41/6d/41bcd70f741ab4a445c368565a67586d.png" alt="">
|
||||
|
||||
所以我们看Dew的架构,其自身也是一个典型的主从结构的大数据系统。跟所有其他的大数据系统一样,Dew也要有一套底层通信体系和消息传输机制。
|
||||
|
||||
当时我们的目标只是想做大数据性能测试与分析,进而优化Spark源代码。所以开发一个分布式大数据性能测试工具是辅助手段,本身不是最主要的目标,所以不可能花太多精力在系统开发上。所以需要寻找一个可以快速开发分布式底层通信体系和消息传输机制的编程框架。
|
||||
|
||||
很快,我们将目标锁定在Akka,这是一个可以同时支持并发编程、异步编程、分布式编程的编程框架,提供了Java和Scala两种编程语言接口,最关键的是Akka非常简单易用。
|
||||
|
||||
最后我们用Akka搭建了Dew的底层通信和消息传输机制,核心代码只有不到100行,花了大半天的时间就开发完成了。一个Master-Slave架构的大数据系统的基本框架就搭建起来了,后面加入分布式集群性能数据采集、日志收集也没花多少时间,很快就输出了我们前面看到的那些Spark性能图表,接着就可以开始对Spark做优化了。
|
||||
|
||||
如果你不太熟悉Akka,看完上面的内容,肯定会对这个如此强大又简单的Akka充满好奇。接下来我们就来看看Akka的原理和应用。
|
||||
|
||||
## Akka原理与应用
|
||||
|
||||
Akka使用一种叫Actor的编程模型,Actor编程模型是和面向对象编程模型平行的一种编程模型。面向对象认为一切都是对象,对象之间通过消息传递,也就是方法调用实现复杂的功能。
|
||||
|
||||
而Actor编程模型认为一切都是Actor,Actor之间也是通过消息传递实现复杂的功能,但是这里的消息是真正意义上的消息。不同于面向对象编程时,方法调用是同步阻塞的,也就是被调用者在处理完成之前,调用者必须阻塞等待;给Actor发送消息不需要等待Actor处理,消息发送完就不用管了,也就是说,消息是异步的。
|
||||
|
||||
面向对象能够很好地对要解决的问题领域进行建模,但是随着摩尔定律失效,计算机的发展之道趋向于多核CPU与分布式的方向,而面向对象的同步阻塞调用,以及由此带来的并发与线程安全问题,使得其在新的编程时代相形见绌。而Actor编程模型很好地利用了多核CPU与分布式的特性,可以轻松实现并发、异步、分布式编程,受到人们越来越多的青睐。
|
||||
|
||||
事实上,Actor本身极为简单,下面是一个Scala语言的Actor例子。
|
||||
|
||||
```
|
||||
class MyActor extends Actor {
|
||||
val log = Logging(context.system, this)
|
||||
|
||||
|
||||
def receive = {
|
||||
case "test" ⇒ log.info("received test")
|
||||
case _ ⇒ log.info("received unknown message")
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
一个Actor类最重要的就是实现receive方法,在receive里面根据Actor收到的消息类型进行对应的处理。而Actor之间互相发送消息,就可以协作完成复杂的计算操作。
|
||||
|
||||
Actor之间互相发送消息全部都是异步的,也就是说,一个Actor给另一个Actor发送消息,并不需要等待另一个Actor返回结果,发送完了就结束了,自己继续处理别的事情。另一个Actor收到发送者的消息后进行计算,如果想把计算结果返回给发送者,只需要给发送者再发送一个消息就可以了,而这个消息依然是异步的。
|
||||
|
||||
这种全部消息都是异步,通过异步消息完成业务处理的编程方式也叫**响应式编程**,Akka的Actor编程就是响应式编程的一种。目前已经有公司在尝试用响应式编程代替传统的面向对象编程,去开发企业应用和网站系统,如果这种尝试成功了,可能会对整个编程行业产生巨大的影响。
|
||||
|
||||
Akka实现异步消息的主要原理是,Actor之间的消息传输是通过一个收件箱Mailbox完成的,发送者Actor的消息发到接收者Actor的收件箱,接收者Actor一个接一个地串行从收件箱取消息调用自己的receive方法进行处理。这个过程请看下面的图。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/26/13/269b28c63c69444dd9dcb0c3124e0713.png" alt="">
|
||||
|
||||
发送者通过调用一个Actor的引用ActorRef来发送消息,ActorRef将消息放到Actor的Mailbox里就返回了,发送者不需要阻塞等待消息被处理,这是和传统的面向对象编程最大的不同,对象一定要等到被调用者返回结果才继续向下执行。
|
||||
|
||||
通过这种异步消息方式,Akka也顺便实现了并发编程:消息同时异步发送给多个Actor,这些Actor看起来就是在同时执行,即并发执行。
|
||||
|
||||
当时Dew使用Akka,主要用途并不是需要Akka的并发、异步特性,而是主要用到它的分布式特性。
|
||||
|
||||
Akka创建Actor需要用ActorSystem创建。
|
||||
|
||||
```
|
||||
val system = ActorSystem("pingpong")
|
||||
|
||||
val pinger = system.actorOf(Props[Pinger], "pinger")
|
||||
|
||||
```
|
||||
|
||||
当Actor的Props配置为远程的方式,就可以监听网络端口,从而进行远程消息传输。比如下面的Props配置sampleActor监听2553端口。
|
||||
|
||||
```
|
||||
akka {
|
||||
actor {
|
||||
deployment {
|
||||
/sampleActor {
|
||||
remote = "akka.tcp://sampleActorSystem@127.0.0.1:2553"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
所以使用Akka编程,写一个简单的Actor,实现receive方法,配置一个远程的Props,然后用main函数调用ActorSystem启动,就得到了一个可以远程通信的JVM进程。使用Akka,Dew只用了100多行代码,就实现了一个Master-Slave架构的分布式集群。
|
||||
|
||||
## 小结
|
||||
|
||||
现在微服务架构大行其道,如果用Akka的Actor编程模型,无需考虑微服务架构的各种通信、序列化、封装,只需要将想要分布式部署的Actor配置为远程模式就可以了,不需要改动任何一行代码。是不是很酷呢?
|
||||
|
||||
此外,Actor的交互方式看起来是不是更像人类的交互方式?拜托对方一件事情,说完需求就结束了,不需要傻傻地等在那里,该干嘛干嘛。等对方把事情做完了,再过来跟你说事情的结果,你可以根据结果决定下一步再做什么。
|
||||
|
||||
人类社会的主要组织方式是金字塔结构,老板在最上面,各级领导在中间,最下面是普通干活的员工。所以一个理想的Actor程序也是同样,采用金字塔的结构,顶层Actor负责总体任务,将任务分阶段、分类以后交给下一级多个Actor,下一级Actor拆分成具体的任务交给再下一级更多的Actor,众多的底层Actor完成具体的细节任务。
|
||||
|
||||
这种处理方式非常符合大数据的计算,大数据计算通常都分成多个阶段,每个阶段又处理一个数据集的多个分片,这样用Actor模型正好可以对应上。所以我们看到有的大数据处理系统直接用Akka实现,它们程序简单,运行也很良好,比如大数据流处理系统[Gearpump](http://gearpump.apache.org/overview.html)。
|
||||
|
||||
## 思考题
|
||||
|
||||
我们前面提到,Akka的远程Actor可以实现分布式服务,我在专栏第15期的思考题提到过基于消息的流式架构,那么能否用Akka实现一个流式的分布式服务呢?如果可以,对于一个典型的Web请求,比如注册用户,这样的流式分布式服务处理过程是什么样的呢?
|
||||
|
||||
欢迎你点击“请朋友读”,把今天的文章分享给好友。也欢迎你写下自己的思考或疑问,与我和其他同学一起讨论。
|
53
极客时间专栏/从0开始学大数据/模块三 大数据开发实践/25 | 模块答疑:我能从大厂的大数据开发实践中学到什么?.md
Normal file
53
极客时间专栏/从0开始学大数据/模块三 大数据开发实践/25 | 模块答疑:我能从大厂的大数据开发实践中学到什么?.md
Normal file
@@ -0,0 +1,53 @@
|
||||
<audio id="audio" title="25 | 模块答疑:我能从大厂的大数据开发实践中学到什么?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/91/3a/91740f59a5bd96456642982e345cdc3a.mp3"></audio>
|
||||
|
||||
你好,我是李智慧,又到了我们模块答疑的时间了。在这个模块里,我主要讲了大数据开发的实践,所以今天我想和你聊聊我在大厂里学到哪些经验。
|
||||
|
||||
软件编程大体上可以分成两种,一种是编写的程序直接供最终用户使用,针对用户需求进行开发,可以说绝大多数工程师开发的绝大多数程序都属于这一种;还有一种是编写的程序供其他工程师使用,大到全球通用的各种编程语言、编程框架、虚拟机、大数据系统,小到公司内部,甚至团队内部自己开发的各种工具、框架,以及应用系统内的非业务模块,都是属于这一种。
|
||||
|
||||
一般说来,后一种编程因为输出的程序要给其他工程师使用,接受专业同行的审视,而且被复用的次数更多,更偏向底层,所以通常技术难度更高一点,开发这样的软件对工程师的技能提升也更高一点。技术产品难度有难易之分,正如工程师水平也分高下,但是两者之间却没有必然联系。
|
||||
|
||||
这些年,我在各种不同的公司工作过,在几个人的小作坊开发过只有几个人使用的所谓ERP系统,也在所谓的大厂参与过全球顶级的大数据系统的开发,据我所见,优秀的人哪里都有,大厂里优秀工程师更多一些,但是小作坊里有时候也卧虎藏龙。
|
||||
|
||||
导致工程师技术水平不同的不在于是大厂还是小作坊,大厂里有十几年如一日拧螺丝钉的人,在一个极其狭窄的技术产品里重复技术细节的工作,对这些年的技术进步几乎一无所知;小作坊也有自己开发整套技术框架的人,虽说是重复造轮子,但是因为造过,所以对软件开发的关键技术和架构设计有更深刻的领悟,软件设计能力和编程技巧通常也更胜一筹。
|
||||
|
||||
如果你有机会在大厂参与核心产品的开发固然好,如果没有,也大可不必遗憾,决定你技术水平和发展前景的最主要因素,不在于公司,而在于你所做的事。小厂因为人少事多,所以你反而可能有更多机会开发一些有技术难度的软件,比如为提高开发效率而给其他工程师开发一些工具,或者为公司开发一些框架供所有项目使用。
|
||||
|
||||
但是这些有技术难度的软件,能让你提高技术水平获得更好成长空间的开发工作,通常又不被公司重视,因为小公司做业务尚且忙不过来,去开发什么工具、框架,在老板看来简直是不务正业。而老板也很难慧眼识珠,安排你去做这些看起来不那么要紧的事。所以你需要自己去争取机会,有时候甚至要用自己的业余时间去做,等有了初步效果,能真正提高公司的效率后,你也会得到更多信任和机会去专门持续进行基础技术产品的开发。
|
||||
|
||||
大数据技术领域因为通常不用直接满足最终用户的需求,所以大数据开发者有更多机会去做一些底层技术方面的开发工作,比如开发大数据平台整合公司的数据和各类系统;开发数据爬虫获取外部的数据资源;开发ETL工具转换公司的各类数据,这些技术也是专栏下一个模块的主要内容。通过开发这些软件,一方面可以更好地利用大数据技术实现业务价值,另一方面对自身的技术水平提升也大有帮助。
|
||||
|
||||
前面我说过,身在大厂并不会保证你一定能参与开发有技术含量的产品,更不能保证你的技术能力一定会得到提升。但是我自己在阿里巴巴、在Intel工作时还是学到了很多,前面专栏分享的很多内容,都是我在这些地方学习到的。这里我再和你分享一个我在Intel学到的关于学习的方法。
|
||||
|
||||
在Intel之前,我学习技术主要就是从网上搜索各种乱七八糟的资料,有的时候运气好,资料比较好,学习的速度和掌握的深度就好一些;有时候运气差,就会走很多弯路。但是在Intel,我发现一些比较厉害的同事,他们学习一样新技术的时候,不会到处乱找资料,而是直接读原始论文。通过原始论文掌握核心设计原理以后,如果需要进一步学习,就去官网看官方文档;如果还需要再进一步参与开发,就去读源代码。
|
||||
|
||||
我刚开始读论文时感觉很费劲,但是后面习惯以后,发现读论文真的是最快的学习方法,因为最核心的东西就在其中,一旦看懂,就真的懂了,而且可以触类旁通,整个软件从使用到开发,很多细节通过脑补就可以猜个八九不离十。而且越是优秀的产品,越是厉害的作者,论文反而越是容易读懂,可能是因为这些作者是真的高手,自己理得越清楚,写出来的论文越是脉络清晰、结构合理、逻辑严谨。
|
||||
|
||||
后来在学习区块链的时候,读原始论文很快就理解了个中关键,反而在跟一些所谓“资深”区块链人士交流的时候,发现他们在一些关键细节上常常犯迷糊,我就感到很诧异,中本聪、布特林在他们的论文中不是说得很清楚嘛。
|
||||
|
||||
下面我顺着今天的话题,来回答一下“sunlight001”同学的问题。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/4f/be/4fc15e708f323bbe3685c1585051e4be.png" alt="">
|
||||
|
||||
我认为,软件开发是一个实践性活动,不管是学习还是应用,最终都需要落到实践中。大数据技术也不例外,没有实践,就不可能深入,想要学好大数据,一定要实践。
|
||||
|
||||
而实践可以分为几个不同的层次。
|
||||
|
||||
**第一个层次是练习实践**,我的专栏剖析架构原理居多,这是专栏定位决定的,而且学习大数据真正的难度,或者说决定你技术高度的依然是你是否理解了大数据技术的核心原理。但是大数据的学习一定要配合练习实践,不管是Hadoop、Spark、Hive的部署,还是编程练习实践,网上的教程都有很多,step by step入门学习的资料也很多。通过这些练习实践,结合专栏的原理分析,可以由表及里,从如何上手操作,到理解背后的原理机制,最后能够做到融会贯通。我看到专栏评论里很多同学贴了代码上来,一边学习一边实践,我们向这些同学学习。
|
||||
|
||||
通过练习实践和原理学习,掌握的是大数据技术的核心关键,真正对一个技术的掌握是需要掌握其细节,没有经过时间的积累,没有在应用中踩过各种坑、遇到各种挑战,没有对各种大数据技术思考再思考、研究再研究,就不可能掌握细节。所以,**大数据实践的第二个层次是应用实践**,在应用中解决问题,在实践中训练自己。
|
||||
|
||||
关于公司没有接触大数据的机会,一般是两种情况,公司没有用大数据,或者公司用大数据技术,但是你接触不到。对于前一种情况,大数据的价值已经成为普遍共识,你要想办法给老板献计献策,同时在同事间鼓吹大数据的好处,让老板关注大数据、使用大数据。如果最后老板决定使用大数据,那么他想到的第一个应该就是你,你的机会也就来了。
|
||||
|
||||
对于后一种情况,如果你已经经过前面的学习和练习实践,掌握了一定的大数据技术知识,申请转岗也可以,在自己的项目中引入大数据和大数据团队展开更多合作也可以,具体也会有很多办法获得应用实践的机会。
|
||||
|
||||
大数据实践的**第三个层次是开发实践**,大数据产品开发有两种,一种是重新开发,比如前面讲过的Doris、Dew,自己从头设计开发一个大数据系统,这样对学习的好处是可以更深刻、更全面理解大数据。另一种就是参与开源大数据产品的开发,比如前面讲过的Spark源码优化,这样的好处是可以和全世界最顶级的工程师一起讨论问题,通过交流学习提高。我在参与Spark开发的时候,跟Databricks、Cloudera的工程师交流,这些人可能是大数据技术领域最顶级的工程师,跟他们交流收获最深刻的不是技术,而是对他们技术水平的判断,以及进而对自己技术水平的判断,并因此促使自己思考自己未来的技术发展之路与人生之路。
|
||||
|
||||
最后我想说的是,这个世界不是为你而存在的,别人根本不会在乎你的感受和你的问题,不会把你想要的东西装在精美的礼盒里打上蝴蝶结送到你的面前,也不会因为你想学习大数据而给你一个实践的机会。不过这样也好,你也不必在乎这个世界怎么看你,只要你想要,你就可以拼尽全力为自己去争取,你要为自己创造机会。
|
||||
|
||||
文章最后,我将Dr.ZZZ、纯洁的憎恶、吴科、galen这几位同学的留言,贴在今天的文稿里分享给你,希望同学们的思考也能对你有所启发。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/95/77/95e4069baa174fa42602ee7f38a1cf77.png" alt=""> <img src="https://static001.geekbang.org/resource/image/d8/32/d82acc254cbfe5b60bcf2ee42460e232.png" alt=""><img src="https://static001.geekbang.org/resource/image/7b/5f/7beab95eb0ac29717950957573bf5b5f.png" alt=""><img src="https://static001.geekbang.org/resource/image/cf/d6/cfb14bd51f468d09703503a8acc18bd6.png" alt="">
|
||||
|
||||
如果你身边也有感到迷茫困惑的朋友,欢迎你点击“请朋友读”,把今天的文章分享给好友。也欢迎你写下自己的思考或疑问,与我和其他同学一起讨论。
|
||||
|
||||
|
Reference in New Issue
Block a user