Presto系列 | 五Tuning Presto SQL Query

Posted 雨钓Moowei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Presto系列 | 五Tuning Presto SQL Query相关的知识,希望对你有一定的参考价值。

Tuning Presto SQL Query

在前文中介绍了Presto中基于成本的优化. 需要再次强调的是:SQL是一种声明式的语言,用户指定他想要的数据,而不需要像编程语言那样需要用户自己指定如何获取这个结果,获取结果的步骤是由Query Planer以及Optimizer优化器去决定;而获取结果数据对应的步骤被称为 *** Query Plan *** 即执行计划

在大多数情况下,使用Presto的用户不需要关心具体的执行计划的细节,用户只需要提交SQL,剩下的工作Presto可以很快的完成并返回给用户想要的结果;

但是,有时候SQL查询会很慢,并没有达到你期望的性能,此时你可能需要优化你的SQL,你首先需要确定是一个特殊的查询性能出问题了,还是一批具有相关参数的查询都比较慢;

首先我们从调优单个查询开始,假设只有一个SQL性能较差,且其余的查询在系统中运行良好;在检查性能较差的查询时,你首先需要检查你所查询的表是否具有统计信息;在撰写本文时通过Hive Connector查询的表均具有统计信息;
例如:

SHOW STATS FOR flights;

通常SQL 中JOIN 操作的代价是比较大的,在测试查询性能时,你需要重点关注JOIN操作 ,可以使用EXPLAIN 查看JOIN的顺序,具体如下:

EXPLAIN
SELECT f.uniquecarrier, c.description, count(*) AS ct
FROM postgresql.airline.carrier c,hive.ontime.flights_orc f
WHERE c.code = f.uniquecarrier
GROUP BY f.uniquecarrier, c.description
ORDER BY count(*) DESC
LIMIT 10;

一般的我们希望较小的表作为build side(当使用JOIN时Presto默认将左边的表作为probe side 将JOIN右侧的表作为build side),因为作为build Side的表会被读取并在内存中构建HASH Table,因此我们希望这个表是较小的那个表;因此在确定JOIN 的顺序是否合适时,需要对参与JOIN的表的数据有所了解,例如,如果你对参与JOIN的表的数据没有任何了解,那你可能需要运行一些额外的查询来获取这些信息;

如果你已经确定JOIN的顺序不是最优的,你可以通过配置config.properties文件中的一个参数(optimizer.join-reordering-strategy)以便对SQL进行优化,该参数会重新调整JOIN时表的顺序;当然如果你只想该参数在当前查询中生效的话,可以配置Session的属性:join_reordering_strategy,这样该参数只会对当前SQL生效,其余的SQL不会生效;
该参数的值可以配置为:AUTOMATIC , ELIMINATE_CROSS_JOINS 以及 NONE当参数设置为ELIMINATE_CROSS_JOINS 或者 NONE时会使用基于成本的优化策略(CBO: cost-based optimizer前文曾详细解释过),其中ELIMINATE_CROSS_JOINS是一个不错的选项,它可以对JOIN的顺序进行重排序以避免产生Cross JOIN(即笛卡尔积)否则查询将会使用用户编写的SQL顺序进行JOIN.
上面的SQL优化前为;

...
FROM postgresql.airline.carrier c,
hive.ontime.flights_orc f
...

优化后

...
FROM hive.ontime.flights_orc f,
postgresql.airline.carrier c
...

关于CBO(cost-based optimizer)

默认情况下CBO一次只会重排9个表,当表的数量大于9时,例如查询中有20个表时,此时Presto首先对前9个表进行重排优化,然后对第二组的9个表进行重排优化,最后剩余两个表,之所以有这样的限制是因为排序的可能数是随着表的个数增加而阶乘式递增的,因此需要设置一个合理的值,当然你可以通过配置config.properties文件中的optimizer.max-reordered-joins参数来增大这个值,需要注意的是如果这个值设置的过高,会影响查询的性能,因为Presto在优化查询时需要花费大量的时间;最后需要注意的是,CBO的目的不是得到最优的方案,而是一个足够好的方案.

除了JOIN优化之外,Presto还包含了一些heuristic-based启发式的优化器.需要注意的是这些优化器并非总能得到最好的结果;
它利用Presto是分布式查询引擎这一事实,聚合操作是并发执行的,这就意味着,聚合操作可以被切分成很多小的单元分发到多个Worker节点上并发执行,最后在汇集到一个节点聚合得到最终的结果;

Presto和其他SQL执行引擎中一个常见的优化策略就是在JOIN之前进行局部的聚合操作,以减少JOIN时的数据量,想要开启该功能可以配置参数:push_aggregation_through_join. 但需要注意的是使用这种策略对数据是有要求的,如果数据不满足条件可能导致查询变慢;一般经验而言,建议先在session中关闭该参数(即设为false)先查看性能;
例如对于一下查询语句

EXPLAIN SELECT count(*) FROM flights_orc;

对应的执行计划:

---------------------------------------------------------------------
- Output[_col0]
        Layout: [count:bigint]
        _col0 := count
    - Aggregate(FINAL)
            Layout: [count:bigint]
            count := "count"("count_3")
        - LocalExchange[SINGLE] ()
                Layout: [count_3:bigint]
            - RemoteExchange[GATHER]
                    Layout: [count_3:bigint]
                - Aggregate(PARTIAL)
                        Layout: [count_3:bigint]
                        count_3 := "count"(*)
                    - TableScan[hive:ontime:flights_orc]
                            Layout: []
(1 row)

假如表中有很多行数据,但是分组键却只有很少值,(即区分度很高,例如针对性别字段聚合)此时这种优化的效果会很好.
然而,当分组键的唯一值很多时,此时需要一个大的Hash Table才能处理大量的数据,默认情况下用于构建HashTable的内存是16M,但是可以通过config.properties文件种的task.max-partial-aggregation-memory参数进行调整;此外因为唯一值太多部分聚合操作,无法起到减少数据量的作用,反而因为多了一个聚合操作导致性能下降;

以上是关于Presto系列 | 五Tuning Presto SQL Query的主要内容,如果未能解决你的问题,请参考以下文章

Presto系列 | 三Presto Architecture

Presto系列 | 三Presto Architecture

Presto系列 | 二Presto Web UI

Presto系列 | 二Presto Web UI

Presto系列 | 一Presto SQL On Everything

Presto系列 | 一Presto SQL On Everything