这是《漫谈分布式系统》系列的第 21 篇,预计会写 30 篇左右。每篇文末有为懒人准备的 TL;DR,还有给勤奋者的关联阅读。扫描文末二维码,关注公众号,听我娓娓道来。也欢迎转发朋友圈分享给更多人。
贴近性能优化的本质
上一篇文章,简单介绍了基于规则的优化 RBO,最后提到 RBO 由于是从经验归纳而来的,所以无法解决很多问题。
根本原因在于经验归纳这个方法是有缺陷的,很难面面俱到。
比如查询走索引的例子,如果 value 分布非常不均衡,那走索引反倒不如全表扫描。
再比如多表 join 的情况,哪个表放前面更好,显然是无法总结出固定规则的。
总之,抽象过的 RBO 太间接以至于脱离实际情况,所以我们需要一个更加直接和贴近细节的优化方法。
先思考几个更加基本的问题。
性能优化的目标是什么?
更短的执行时间,
更低的延迟,
更大的吞吐量,
更少的资源使用,
......
性能优化的重点是什么?
在分布式程序里面,我们重点关注了 Shuffle,因为涉及非常多 IO,IO 是最拖累性能的,
在 SQL Join 里面,我们除了关注 Shuffle,还考虑了 Match,因为涉及很多 IO 和计算量,
在 Spark ML 里,我们通过 cache 数据来避免重复迭代计算,因为计算量实在太大,
......
好像越想越复杂了,但这还没完,还有句话叫做:如果你无法度量,就无法优化。
那性能又该怎么度量?
在常规算法里,我们通过时间复杂度和空间复杂度来衡量算法好坏,
那在分布式计算里呢,在 SQL 里呢?
时间复杂度和空间复杂度?似乎有些启发。
时间复杂度,可以对应计算量;空间复杂度,不就是内存占用吗?
这样看来,我们可以类似地,从资源占用的角度来设计分布式程序的性能度量方法。
于是问题变成,一个分布式程序会使用哪些系统资源?
先粗略地看下,对一个程序而言,会使用到一台计算机的主要资源有这些:
CPU
Memory
IO
-
Disk IO
Network IO
扩展到分布式程序下的多个节点,考虑到这篇文章的主题,以及我们最近关注的 Hive 和 Spark 都大量处理 HDFS 上的数据,而从 HDFS 读取数据,既有可能从本地磁盘读取,又有可能从网络拉取,并且无法事先确定,所以需要引入 HDFS IO。
而另一方面,对内存的使用是很难事先计算好的。这很好理解,比如我们前面讲过的 Shuffle 和 Join,对不同的数据量和参数设置,可能采取不同的算法,过程中还有 spill 等操作,再加上实际数据对压缩率的影响等等,都导致对内存使用的估算,是很难准确的。
基于这些考虑,我们对包括 SQL 在内的分布式程序的资源消耗,主要考虑这几方面:
CPU
IO
-
Disk IO
Network IO
HDFS IO
我们做性能优化的目的,