现象:因业务需求新增了 SQL 任务,这 SQL 扫描的表为分区表,且 SQL 条件里表只指定了一个分区,按指定的分区来看数据量并不大,但是 SQL 的费用非常高。费用比预想的结果相差几倍甚至 10 倍以上。
若只知道总体费用暴涨,但是没明确是什么任务暴涨,可以可以参考查看账单详情 - 使用记录文档,找出费用异常的记录。
分析:我们先明确 MaxCompute SQL 后付费的计费公式:一条 SQL 执行的费用 = 扫描输入量 ️ SQL 复杂度 ️ 0.3(¥/GB)。
变量主要是输入量和复杂度,但实际上复杂度最高也就为 4,由复杂度引起的费用暴涨是比较罕见,我们不妨先把排查重点放在输入量上。
排查:查看 Logview 的 inputs 信息
如上图会发现 input 的分区量是 14 个,这个与预想的(SQL 条件中只指定一个分区)不一致。问题就出在这里,此时基本可以判断这个 SQL 的分区并没有裁剪好,也就是说最终输入量不是一个分区而是多个或者全表。
输入的分区量和预计的不一致,排除 SQL 中确实没有对分区设置条件这因素,那么就是分区裁剪失效了。
已知的分区裁剪失效场景主要有:分区条件用了自定义函数进行裁剪;在 Join 关联时的 Where 条件中也有可能会失效。
执行 explain sql 语句;
看执行结果,读取的分区都有哪些,如执行explain select seller_id from xxxxx_trd_slr_ord_1d where ds=rand();
结果如下:
看上图中红框的内容,表示读取了表 xxxxx_trd_slr_ord_1d 的 1344 个分区,即该表的所有分区,如果直接执行这个 sql,最终会因为全表扫描导致输入量增加从而费用增加。
关于分区裁剪失败场景 (使用函数或者跟 join 关联有关的场景) 分析可以参考文档《分区剪裁合理性评估》。大家在执行 sql 前如果对分区的裁剪有疑虑,不放执行一次 explain sql 语句;
再执行 SQL 语句。
关于分区条件用自定义函数或者内置函数导致分区裁剪失效的解决方案:
- 内置函数目前已经都支持进行分区裁剪。
-
自定义函数需要支持分区裁剪有两种方式:
- 在编写 UDF 的时候,UDF 类上加入 Annotation。
@com.aliyun.odps.udf.annotation.UdfProperty(isDeterministic=true)
- 在编写 UDF 的时候,UDF 类上加入 Annotation。
> 注意:com.aliyun.odps.udf.annotation.UdfProperty 定义在 odps-sdk-udf.jar 文件中。您需要把引用的 odps-sdk-udf 版本提高到 0.30.x 或以上。* 在 SQL 语句前设置 Flag:`set odps.sql.udf.ppr.deterministic = true;`,此时 SQL 中所有的 UDF 均被视为 deterministic。该操作执行的原理是做执行结果回填,但是结果回填存在限制,即最多回填 1000 个 Partition。因此,如果 UDF 类加入 Annotation,则可能会导致出现超过 1000 个回填结果的报错。此时您如果需要忽视此错误,可以通过设置 Flag:`set odps.sql.udf.ppr.to.subquery = false;` 全局关闭此功能。关闭后,UDF 分区裁剪也会失效。
本文作者:海清
阅读原文
本文为云栖社区原创内容,未经允许不得转载。