计算平台追求目标:如何降低计算资源的消耗,提高任务执行的性能,提升任务产出的时间。
1.系统优化
1.1 HBO (History-Based Optimiz町, 基于历史的优化器)
系统中存在大量的周期性调度的脚本(物理计划稳定),且这些脚本的输入一般比较稳定。根据任务的执行历史为其分配更合理的计算资源。
HBO 一般通过自造应调整系统参数来达到控制计算资源的目的。 提高 CPU 利用率 、提高内存利用率 、提高 Instance 并发数 、降低执行时长。
HBO 是基于执行历史来设置计划的,对于日常来说,数据量波动不大,工作良好。但是某些任务在特定场合下依旧有数据量暴涨的情况发生,尤其是在大促“双 l l ”和“双 12”期间,这个日常生成的 HBO计划就不适用了。
1.2 CBO (Cost-Based Optimizer , 基于代价的优化器)
收集到的表、分区、索引等统计信息来计算每种执行方式的代价( Cost ),进而选择其中代价最少的执行方式。
但对表和列上统计信息的收集也是有代价的,收集统计信息会消耗大量资源。MaxCompute 采用各种抽样统计算法,通过较少的资源获得大量的统计信息,最大化提高统计信息利用率。
2.任务优化
每个输入分片会让一个 Map Instance 来处理,在默认情况下,以Pangu 文件系统的一个文件块 的大小(默认为 256MB )为一个分片。
Map Instance 输出的结果会暂时放在一个环形内存缓冲区中,当该缓冲区快要溢出时会在本地文件系统中创建一个溢出文件, 即 Write Dump。Map读取的时可以调节 Map Instance 的个数 ,也可以调节单个Map Instance读取文件的个数。
在写人磁盘之前,线程首先根据 Reduce Instance 的个数划分分区,数据将会根据 Key 值 Hash 到不同的分区上,一个 Reduce Instance 对应一个分区的数据。
Map 端也会做部分聚合操作,以减少输入 Reduce 端的数据量。
Reduce 端负责的是对 Map 端梳理后的有序 key-value 键值对进行聚合,即进行 Count 、 Sum 、 Avg 等聚合操作,得到最终聚合的结果。
2.1 Map倾斜
Map 端是 MR 任务的起始阶段, Map 端的主要功能是从磁盘中将数据读人内存。在 Map 端读数据时,由于读人数据的文件大小分布不均匀,因此会导致有些 Map Instance 读取并且处理的数据特别 多,而有些 Map Instance 处理的数据特别少,造成 Map 端长尾。以下两种情况可能会导致 Map 端长尾:
•上游表文件的大小特别不均匀,并且小文件特别多,导致当前表Map 端读取的数据分布不均匀,引起长尾。
可通过对上游合并小文件,同时调节本节点的小文件的参数来进行优化(第一个参数是调节 Map 任务的 Map Instance 的个数;第二个参数用于调节单个 Map Instance 读取的小文件个数 )。
• Map 端做聚合时,由于某些 Map Instance 读取文件的某个值特别多而引起长尾,主要是指 Count Distinct 操作 。
使用随机分发函数将 Map 端分发后的数据,重新按照随机值再进行一次分发。 原先不加随机分发函数时, Map 阶段需要与使用MapJoin 的小表进行笛卡儿积操作, Map 端完成了大小表的分发和笛卡儿积操作。 使用随机分布函数后, Map 端只负责数据的分发,不再有复杂的聚合或者笛卡儿积操作,因此不会导致 Map 端长尾。
2.2 Join倾斜
Join 执行阶段会将 Join Key 相同的数据分发到同一个执行 Instance 上处理 。如果某个Key 上的数据量比较大,则会导致该 Instance 执行时间较 长。
在执行日志中该 Join Task 的大部分 Instance 都已执行完成,但少数几个 Instance 一直处于执行中(这种现象称之为长尾)。 场景主要分三类:
•Join 的某路输入比较小,可以采用 MapJoin ,避免分发引起长尾。小表duplicate
• Join 的每路输入都较大,且长尾是空值导致的,可以将空值处理成随机值,避免聚集。因为空值无法关联上,只是分发到一处 , 因此处理成随机值既不会影响关联结果,也能很好地避免聚焦导致长尾。
• Join 的每路输入都较大,且长尾是热点值导致的。 可以先将热点 key 取出,对于主表数据用热点 key 切分成热点数据和非热点数据两部分分别处理,最后合并。
2.3 Reduce倾斜
MaxCompute 中 Distinct 的执行原理是将需要去重的宇段以及 Group By 宇段联合作为 key 将数据分发到 Reduce 端。
因为 Distinct 操作,数据无法在 Map 端的 Shuffle 阶段根据 Group By先做一次聚合操作,以减少传输的数据量,而是将所有的数据都传输到Reduce 端,当 key 的数据分发不均匀时,就会导致 Reduce 端长尾。
Reduce 端产生长尾的主要原因就是 key 的数据分布不均匀。 由于数据是根据 Hash 分配的,因此也会导致有些 Reduce Instance 会分配到大量数据,而有些Reduce Instance 却分配到很少数据,甚至没有分配到数据。 如下几种情况会造成 Reduce 端长尾:
• 对同 一个表按照维度对不同的列进行 Count Distinct 操作,造成Map 端数据膨胀,从而使得下游的 Join 和 Reduce 出现链路上的长尾。
• Map 端直接做聚合时出现 key 值分布不均匀,造成 Reduce 端长尾。
可以对热点 key 进行单独处理,然后通过“Union All”合并。
• 动态分区数过多时可能造成小文件过多,从而引起 Reduce 端长尾。
可以把符合不同条件的数据放到不同的分区,避免通过多次“ Insert Overwrite,,写人表中,特别是分区数比较多时,能够很好地简化代码。
• 多个 Distinct 同时出现在一段 SQL 代码中时,数据会被分发多次,不仅会造成数据膨胀 N 倍,还会把长尾现象放大 N 倍。