PolarDB为DBA提供了一个非常厉害的大杀器—optimizer trace,通过它我们可以了解到每个SQL是如何被解析、优化并到最终执行的。在其中我们可以清楚的看到并行优化器是如何生成并行执行计划,如果SQL不能被并行化,就会给出清晰的理由。看到这里,终于对改善慢如老牛的查询有了一点点信心,也许我们慢如老牛的查询并未如我们所愿在并行的快车道上执行呢?下面我们就以案例来分析trace的灵活运用。

Optimizer trace并不是自动就会默认开启的,开启trace多多少少都会有一些额外的工作要做,因此并不建议一直开着。但trace属于轻量级的工具,开启和关闭都非常简便,对系统的影响也微乎其微。而且支持在session中开启,不影响其它session,对系统的影响降到了最低。

如果发现某个SQL有问题,只需要在session中设置optimizer_trace,将trace开启即可,当不再需要时,直接关闭即可。

SET SESSION optimizer_trace=”enabled=on”;

然后执行有问题的SQL,如果SQL的执行时间很长的话,也可以只进行explain 操作,即:

EXPLAIN your SQL;

最后,通过

SELECT * FROM information_schema.OPTIMIZER_TRACE\G

查询即可得到trace信息。trace信息以json格式输出,通过\G可以格式化输出trace信息,更宜于阅读。如下所示:

下面我们以实例来分析一下trace在实践中的应用。TPCH的数据的scale为1G,以tpch的query 5为例:

TPCH的query5是一个多表JOIN,其中customer,orders,lineitem,supplier表比较大,nation和region比较小。

首先我们来看一下未开启并行的查询计划,如下所示: pic

开启并行的SQL如下:

SET SESSION MAX_PARALLEL_DEGREE=16; //设置最大并行度为16

比较串行的查询计划和并行的查询计划,可以发现有些不同之处:

  • 后分别以串行和并行方式执行Query 5语句:结果如下:

从结果中可以看到,性能提升了大约150%,那么还有没有提升的空间呢?

下来我们来看一下并行计划的trace。 pic

在trace的输出中有很多项,这里我们主要看是如何选择并行执行计划的。上图中可以看到一些并行计划的基础条件检查:

  • max_parallel_degree:表示最大并行度,当其为0时,表示不允许选择并行计划。
  • max_parallel_workers:表示系统同时允许的最大worker线程数。当可用worker线程数不足时,不允许选择并行计划;
  • serializable_isolation:是当前事务的隔离方式,串行化隔离方式的事务不支持并行化;
  • multi_stmt_transaction_mode:表示单语句事务或长事务,目前只支持单语句事务的并行化;

当这些条件已经满足,则开始选择可能并行扫描的表:

在potential_parallel_tables列表中会显示此语句中潜在的可能并行化的表。

在considered_parallel_tables子项中会依次检查潜在表,以确定可以并行化的表。

pic

每个子项如上图所示,其中包含表名、访问类型、是否支持并行化等信息,其中与并行化有关的最重要信息是

  • partitions:表示表用于并行化的分片数,如表customer是125个分片;
  • chosen:表示当前片已经被候选为并行化表,如之后再没有其它表被选中,则最后一个被选中的表就是并行化表;

prefix_cost_too_large表示到目前为止cost已经太大,无法继续选择其它表作为并行化表。

下面我们来看如何优化提升Query 5的性能:

从trace中我们可以看到,可并行化的表customer的efficient_partitions为13,而我们设置的最大并行度为16,也就是说最大可以有16个worker可以使用,但任务分片却只有13个,显然没有充分利用所有资源。

通过分析所有可选择的表我们发现还有orders、lineitem表也是很大的表,若是选择其它表是不是就可以充分利用这些资源呢?

我们来看下orders,如何让优化器先尝试orders表呢?其中可以通过hint:join_order()来改变Join的顺序来间接实现选择并行化表的顺序。

hint如下:/*+ join_order(orders, customer) */

然后我们再来看一下并行的查询计划:

pic

与没有hint的查询计划相比,会发现JOIN的表顺序发生了变化,orders表与customer表交换了顺序,并且orders表的 Parallel scan (16 workers)变成为16个worker。

下面我们重新做下测试:

Query 5串行执行(秒)并行执行(秒) DOP=16并行执行—hint DOP=16
Round - 14.001.710.75
Round - 23.961.600.76
Round - 33.981.550.78
Round - 43.961.580.77
Round - 53.971.540.77
Avg3.9741.5960.766

通过测试发现,通过修改join_order后,发现性能有明显提升,对比串行计划提升大约420%,对比未hint的性能提升大约100%。 另外,也对其它表做并行化进行了测试,结果与customer表并行化的结果相关不大。

通过trace,我们可能发现一些我们在explain中看不到的东西,当发现query并未产生并行查询计划时,可以将trace打开,可以协助我们发现查询不能并行化的原因,针对这些原因可以进行调整,如增加资源、调整参数、转换存储引擎、修改JOIN顺序等。