使用 split_size 优化的 ODPS SQL 的场景
首先有两个大背景需要说明如下:
说明 1:split_size,设定一个 map 的最大数据输入量,单位 M,默认 256M。用户可以通过控制这个变量,从而达到对 map 端输入的控制。设置语句:set odps.sql.mapper.split.size=256。一般在调整这个设置时,往往是发现一个 map instance 处理的数据行数太多。
说明 2:小文件越多,需要 instance 资源也越多,MaxCompute 对单个 Instance 可以处理的小文件数限制为 120 个,如此造成浪费资源,影响整体的执行性能(文件的大小小于块 Block 64M 的文件)。
场景一:单记录数据存储太少
原始 Logview Detail:
可以发现 Job 只调起一个 Map Instance,供处理了 156M 的数据,但这些数据共有 5 千多万的记录(单记录平均 3 个 byte),花费了 25 分钟。此外,从 TimeLine 看可以发现,整个 Job 耗费 43 分钟,map 占用了超过 60% 的时间。故可对 map 进行优化。
优化手段:调小 split_size 为 16M
优化之后的 logview:
优化后,可以发现,Job 调起了 7 个 Map Instance,耗时 4 分钟;某一个 Map 处理了 27M 的数据,6 百万记录。(这里可以看出 set split_size 只是向 Job 提出申请,单不会严格生效,Job 还是会根据现有的资源情况等来调度 Instance)因为 Map 的变多,Join 和 Reduce 的 instance 也有增加。整个 Job 的执行时间也下降到 7 分钟。
场景二:用 MapJoin 实现笛卡尔积
原始 logview:
可以发现,Job 调起了 4 个 Map,花费了 3 个小时没有跑完;查看详细 Log,某一个 Map 因为笛卡尔的缘故,生成的数据量暴涨。综合考虑,因为该语句使用 Mapjoin 生成笛卡尔积,再筛选符合条件的记录,两件事情都由 map 一次性完成,故对 map 进行优化。
策略调低 split_size
优化后的 logview:
优化后,可以看到,Job 调度了 38 个 map,单一 map 的生成数据量下降了,整体 map 阶段耗时也下降到 37 分钟。回头追朔这个问题的根源,主要是因为使用 mapjoin 笛卡尔积的方式来实现 udf 条件关联的 join,导致数据量暴涨。故使用这种方式来优化,看起来并不能从根本解决问题,故我们需要考虑更好的方式来实现类似逻辑。
本文作者:祎休阅读原文
本文为云栖社区原创内容,未经允许不得转载。