使用列统计信息优化计划
优化器在 CBO 阶段,利用列统计信息,能够做出更为精准的估算,进而找到代价更小的执行计划(plan)。为了有效利用统计信息,Doris 首先需要执行统计信息的收集工作,具体请参考统计信息收集。
在统计信息的辅助下,我们能够更为准确地估算出算子输出的行数,这包括过滤、Join 以及聚合等操作。下面,我们将通过案例来演示 Doris 如何使用列统计信息优化计划。
案例 1:过滤
查询语句如下:
select * from orders where o_orderdate < '1990-01-01'
在没有统计信息的情况下,优化器只能依据经验参数来估算o_orderdate < '1990-01-01'
这一条件过滤后的行数,例如,可能会简单地将过滤结果估算为orders
表行数的一半。
然而,通过执行analyze table orders
命令,优化器便能够获取到o_orderdate
列的最小值,即'1992-01-01'
。因此,优化器能够准确地判断出过滤后的行数实际上并没有减少。
案例 2:Join 操作
Hash Join 是最常用的 Join 算法。该算法会利用一个表来构建 Hash Table,而另一个表则作为 Probe 表进行匹配。由于构建 Hash Table 的代价远高于 Probe 操作代价,因此应选择行数较少的表来构建 Hash Table。
在 Doris 中,规定 Join 操作的右表用于构建 Hash Table,而左表则作为 Probe 表。已知orders
表的行数为 150,000,而customer
表的行数为 15,000,两者相差 10 倍。
查询语句如下:
select * from orders join customer on o_custkey = c_custkey and o_orderdate < '1990-01-01'
在没有统计信息的情况下,我们可能会估算过滤后的orders
表行数为原表的一半,即 75,000 行,这仍然比customer
表的行数多。因此,Join 的顺序会被确定为orders join customer
,即customer
表构建 Hash Table,orders
表作为 Probe 表。
然而,如果拥有统计信息,优化器便能知道o_orderdate
列的最小值是'1992-01-01'
,因此会估算出过滤后的结果为 0 行,这显然比customer
表的行数要少。于是,Join 的顺序会调整为customer join orders
。
在实际测试中,采用统计信息生成的执行计划相较于未使用统计信息的执行计划,其执行效率增加了 40%。
案例 3:均匀假设
在实际业务场景中,数据分布往往并不均匀。以订单日期 o_orderdate
为例,尽管在使用统计信息估算查询计划成本时,优化器可能会采用均匀假设,即假设每年的订单量相同,但实际上,1992 年的订单量可能会显著超过其他年份的总和。具体来说,如果 o_orderdate
的范围是 '1992-01-01' 到 '1998-08-02',一共 8 年,那么在均匀假设下,优化器会估算 o_orderdate < '1993-01-01'
的过滤率为 1/8。然而,这种假设很可能导致优化器低估了实际过滤后的行数,进而影响到后续 Join 操作中表的选择顺序。
为了更准确地评估查询性能并优化 Join 顺序,我们需要查看执行计划(Profile)中记录的实际过滤行数。在此基础上,还可以在 SQL 中添加 Hint,以指导优化器选择更合适的 Join 顺序。
案例 4:无列统计信息
在某些特定场景下,可能会出现无法收集列统计信息的情况。例如,当查询涉及外部表时、当数据量极为庞大,或者收集统计信息的成本过高时。面对这种情况,优化器将转而依据表的行数,并通过启发性规则来生成执行计划(plan)。通常,在缺少统计信息的情况下,优化器倾向于生成一个左深树的执行计划。