暂无图片
暂无图片
3
暂无图片
暂无图片
暂无图片

PostgreSQL17优化器改进(9)MergeAppend执行算子优化

563

PostgreSQL17优化器改进(9)MergeAppend执行算子优化

今天我们来看一下PostgreSQL17优化器对于MergeAppend执行算子的优化,本次是对MergeAppend的cost计算的bug的修复,该bug其实在最初引入MergeAppend执行算子的时候就出现了。通过对MergeAppend执行计划的修正,可以更准确地计算需要排序的行数。在这里我自己理解是,就是经过修正执行计划后,可以通过使用索引已经有序的特点,减少排序数据量。

MergeAppend执行算子存在的问题

在对继承表/分区表进行查询时且需要对查询的数据排序时,为啥没有使用到看起来更优的Merge Append+sort执行计划,而是使用了sort+Append的执行计划。下面我们来测试一下两个版本的执行计划。

执行计划对比

创建测试表

--drop table matest0 cascade;
create table matest0(a int primary key);
create table matest1() inherits (matest0);
insert into matest0 select generate_series(1, 400);
insert into matest1 select generate_series(1, 200);
analyze matest0;
analyze matest1;

查看表结构

在这里我们需要了解一个知识点,就是对于继承表,索引这些对象是无法被继承的。

testdb=# \d matest0
              Table "public.matest0"
 Column |  Type   | Collation | Nullable | Default 
--------+---------+-----------+----------+---------
 a      | integer |           | not null | 
Indexes:
    "matest0_pkey" PRIMARY KEY, btree (a)
Number of child tables: 1 (Use \d+ to list them.)

testdb=# \d matest1
              Table "public.matest1"
 Column |  Type   | Collation | Nullable | Default 
--------+---------+-----------+----------+---------
 a      | integer |           | not null | 
Inherits: matest0

PostgreSQL16.3的执行计划

在PostgreSQL16.3中执行下面的SQL语句,从下面测试用例可知,当对父表查询时,先分别对子表和父表进行查询,然后以Append执行算子把子查询的结果集汇总,然后再进行排序。在这里cost值为19.54。

testdb=# explain select * from matest0 where a < 100 order by a;
                                  QUERY PLAN                                  
------------------------------------------------------------------------------
 Sort  (cost=19.04..19.54 rows=198 width=4)
   Sort Key: matest0.a
   ->  Append  (cost=0.00..11.49 rows=198 width=4)
         ->  Seq Scan on matest0 matest0_1  (cost=0.00..7.00 rows=99 width=4)
               Filter: (a < 100)
         ->  Seq Scan on matest1 matest0_2  (cost=0.00..3.50 rows=99 width=4)
               Filter: (a < 100)
(7 rows)

Time: 2.578 ms

PostgreSQL17的执行计划

在PostgreSQL17中执行下面的SQL语句,可以看到,当对父表查询时,先分别对父表和子表分别排序,然后通过Merge Append执行算子对子查询的结果进行汇总。

testdb=# explain select * from matest0 where a < 100 order by a;
                                            QUERY PLAN                                            
--------------------------------------------------------------------------------------------------
 Merge Append  (cost=6.94..18.90 rows=198 width=4)
   Sort Key: matest0.a
   ->  Index Only Scan using matest0_pkey on matest0 matest0_1  (cost=0.15..9.88 rows=99 width=4)
         Index Cond: (a < 100)
   ->  Sort  (cost=6.78..7.03 rows=99 width=4)
         Sort Key: matest0_2.a
         ->  Seq Scan on matest1 matest0_2  (cost=0.00..3.50 rows=99 width=4)
               Filter: (a < 100)
(8 rows)

Time: 49.665 ms

PostgreSQL17的cost值为18.90,但是PostgreSQL16.3的值为19.54,虽然PostgreSQL17比PostgreSQL16.3的cost降低幅度很小,但是在当前使用CBO规则的优化器的情况也是最优的选择。另外我们也看到了PostgreSQL16.3执行耗时居然比PostgreSQL17都少,这个时候我们是不是应该怀疑是不是执行计划有问题呢?针对这个问题,我自己的理解是对于CBO优化器,只要cost自身估值没有问题,始终还是要以cost的值为准绳来衡量那个执行计划是最优的。另外当选择cost的估值最优时(该测试案例执行时间长),即使导致执行计划变差,影响相对较小;但是当选择cost的估值最差时(该测试案例执行时间短),如果导致执行计划变差,这时对于sql的性能是比较大的。

总结

我们简单对这次的测试进行总结一下。在PostgreSQL17版本中,解决了在对继承表/分区表进行查询且需要排序时,可以使用Merge Append执行算子获取更优的执行计划,在这里可以使用到父表的索引,减少了排序操作应该是降低cost的最主要因素。

– / END / –

可以通过下面的方式联系我

  • 微信公众号:@墨竹札记
  • 墨天轮:@墨竹
  • 微信:wshf395062788
  • PGFans:@墨竹

如果这篇文章为你带来了灵感或启发,就请帮忙点赞收藏转发;如果文章中不严谨或者错漏之处,请及时评论指正。非常感谢!

最后修改时间:2025-02-06 10:59:23
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论