繁体   English   中英

分区表查询仍然扫描所有分区

[英]Partitioned table query still scanning all partitions

我有一张超过十亿条记录的桌子。 为了提高性能,我将其分区为30个分区。 最常见的查询在where子句中有(id = ...) ,所以我决定在id列上对表进行分区。

基本上,分区是以这种方式创建的:

CREATE TABLE foo_0 (CHECK (id % 30 = 0)) INHERITS (foo);
CREATE TABLE foo_1 (CHECK (id % 30 = 1)) INHERITS (foo);
CREATE TABLE foo_2 (CHECK (id % 30 = 2)) INHERITS (foo);
CREATE TABLE foo_3 (CHECK (id % 30 = 3)) INHERITS (foo);
.
.
.

我为整个数据库运行了ANALYZE ,特别是,我通过运行以下方法收集了该表的id列的额外统计信息:

ALTER TABLE foo ALTER COLUMN id SET STATISTICS 10000;

但是,当我运行在id列上过滤的查询时,计划程序会显示它仍在扫描所有分区。 constraint_exclusion设置为partition ,因此不是问题。

EXPLAIN ANALYZE SELECT * FROM foo WHERE (id = 2);


                                               QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------
 Result  (cost=0.00..8106617.40 rows=3620981 width=54) (actual time=30.544..215.540 rows=171477 loops=1)
   ->  Append  (cost=0.00..8106617.40 rows=3620981 width=54) (actual time=30.539..106.446 rows=171477 loops=1)
         ->  Seq Scan on foo  (cost=0.00..0.00 rows=1 width=203) (actual time=0.002..0.002 rows=0 loops=1)
               Filter: (id = 2)
         ->  Bitmap Heap Scan on foo_0 foo  (cost=3293.44..281055.75 rows=122479 width=52) (actual time=0.020..0.020 rows=0 loops=1)
               Recheck Cond: (id = 2)
               ->  Bitmap Index Scan on foo_0_idx_1  (cost=0.00..3262.82 rows=122479 width=0) (actual time=0.018..0.018 rows=0 loops=1)
                     Index Cond: (id = 2)
         ->  Bitmap Heap Scan on foo_1 foo  (cost=3312.59..274769.09 rows=122968 width=56) (actual time=0.012..0.012 rows=0 loops=1)
               Recheck Cond: (id = 2)
               ->  Bitmap Index Scan on foo_1_idx_1  (cost=0.00..3281.85 rows=122968 width=0) (actual time=0.010..0.010 rows=0 loops=1)
                     Index Cond: (id = 2)
         ->  Bitmap Heap Scan on foo_2 foo  (cost=3280.30..272541.10 rows=121903 width=56) (actual time=30.504..77.033 rows=171477 loops=1)
               Recheck Cond: (id = 2)
               ->  Bitmap Index Scan on foo_2_idx_1  (cost=0.00..3249.82 rows=121903 width=0) (actual time=29.825..29.825 rows=171477 loops=1)
                     Index Cond: (id = 2)
.
.
.

我能做些什么让刨床有更好的计划? 我是否需要运行ALTER TABLE foo ALTER COLUMN id SET STATISTICS 10000; 对于所有分区呢?

编辑

在使用Erwin建议的查询更改后,计划程序仅扫描正确的分区,但执行时间实际上比完整扫描(至少是索引)更差。

EXPLAIN ANALYZE select * from foo where (id % 30 = 2) and (id = 2);
                                                                         QUERY PLAN
                                                                             QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Result  (cost=0.00..8106617.40 rows=3620981 width=54) (actual time=32.611..224.934 rows=171477 loops=1)
   ->  Append  (cost=0.00..8106617.40 rows=3620981 width=54) (actual time=32.606..116.565 rows=171477 loops=1)
         ->  Seq Scan on foo  (cost=0.00..0.00 rows=1 width=203) (actual time=0.002..0.002 rows=0 loops=1)
               Filter: (id = 2)
         ->  Bitmap Heap Scan on foo_0 foo  (cost=3293.44..281055.75 rows=122479 width=52) (actual time=0.046..0.046 rows=0 loops=1)
               Recheck Cond: (id = 2)
               ->  Bitmap Index Scan on foo_0_idx_1  (cost=0.00..3262.82 rows=122479 width=0) (actual time=0.044..0.044 rows=0 loops=1)
                     Index Cond: (id = 2)
         ->  Bitmap Heap Scan on foo_1 foo  (cost=3312.59..274769.09 rows=122968 width=56) (actual time=0.021..0.021 rows=0 loops=1)
               Recheck Cond: (id = 2)
               ->  Bitmap Index Scan on foo_1_idx_1  (cost=0.00..3281.85 rows=122968 width=0) (actual time=0.020..0.020 rows=0 loops=1)
                     Index Cond: (id = 2)
         ->  Bitmap Heap Scan on foo_2 foo  (cost=3280.30..272541.10 rows=121903 width=56) (actual time=32.536..86.730 rows=171477 loops=1)
               Recheck Cond: (id = 2)
               ->  Bitmap Index Scan on foo_2_idx_1  (cost=0.00..3249.82 rows=121903 width=0) (actual time=31.842..31.842 rows=171477 loops=1)
                     Index Cond: (id = 2)
         ->  Bitmap Heap Scan on foo_3 foo  (cost=3475.87..285574.05 rows=129032 width=52) (actual time=0.035..0.035 rows=0 loops=1)
               Recheck Cond: (id = 2)
               ->  Bitmap Index Scan on foo_3_idx_1  (cost=0.00..3443.61 rows=129032 width=0) (actual time=0.031..0.031 rows=0 loops=1)
.
.
.
         ->  Bitmap Heap Scan on foo_29 foo  (cost=3401.84..276569.90 rows=126245 width=56) (actual time=0.019..0.019 rows=0 loops=1)
               Recheck Cond: (id = 2)
               ->  Bitmap Index Scan on foo_29_idx_1  (cost=0.00..3370.28 rows=126245 width=0) (actual time=0.018..0.018 rows=0 loops=1)
                     Index Cond: (id = 2)
 Total runtime: 238.790 ms

与:

EXPLAIN ANALYZE select * from foo where (id % 30 = 2) and (id = 2);
                                                                            QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Result  (cost=0.00..273120.30 rows=611 width=56) (actual time=31.519..257.051 rows=171477 loops=1)
   ->  Append  (cost=0.00..273120.30 rows=611 width=56) (actual time=31.516..153.356 rows=171477 loops=1)
         ->  Seq Scan on foo  (cost=0.00..0.00 rows=1 width=203) (actual time=0.002..0.002 rows=0 loops=1)
               Filter: ((id = 2) AND ((id % 30) = 2))
         ->  Bitmap Heap Scan on foo_2 foo  (cost=3249.97..273120.30 rows=610 width=56) (actual time=31.512..124.177 rows=171477 loops=1)
               Recheck Cond: (id = 2)
               Filter: ((id % 30) = 2)
               ->  Bitmap Index Scan on foo_2_idx_1  (cost=0.00..3249.82 rows=121903 width=0) (actual time=30.816..30.816 rows=171477 loops=1)
                     Index Cond: (id = 2)
 Total runtime: 270.384 ms

对于非平凡的表达式,您必须在查询中重复或多或少的逐字条件,以使Postgres查询规划器理解它可以依赖于CHECK约束。 即使看起来多余!

每个文件

启用约束排除后,计划程序将检查每个分区的约束,并尝试证明不需要扫描分区,因为它不能包含满足查询的WHERE子句的任何行。 当规划人员可以证明这一点时 ,它会从查询计划中排除分区。

大胆强调我的。 规划者不理解复杂的表达方式。 当然,这也必须得到满足:

确保在postgresql.conf未禁用constraint_exclusion配置参数。 如果是,则不会根据需要优化查询。

代替


  
 
  
  
    SELECT * FROM foo WHERE (id = 2); 
  

尝试:

SELECT * FROM foo WHERE id % 30 = 2 AND id = 2;

和:

constraint_exclusion的默认(和推荐)设置实际上既不是on也不是off ,而是一个名为partition的中间设置,这使得该技术仅应用于可能在分区表上工作的查询。 on设置使计划程序检查所有查询中的CHECK约束,即使是不太可能受益的简单查询。

您可以尝试使用constraint_exclusion = on来查看计划程序是否捕获而没有多余的逐字条件。 但是你必须权衡这个设置的成本和收益。

替代方案是@harmic已经概述的分区更简单的条件。

否,增加STATISTICS的数量在这种情况下无济于事。 查询中只有CHECK约束和WHERE条件。

不幸的是,在postgresql中分配是相当原始的。 它仅适用于基于范围和列表的约束。 您的分区约束太复杂,查询计划程序无法决定排除某些分区。

手册中它说:

保持分区约束简单,否则规划人员可能无法证明不需要访问分区。 使用简单的相等条件进行列表分区,或使用简单范围测试进行范围分区,如前面的示例所示。 一个好的经验法则是,分区约束应该只包含使用B-tree-indexable运算符将分区列与常量进行比较。

正如Erwin建议的那样,你可能会改变你的WHERE子句,以便明确提到模数表达式。 我过去没有太多运气,虽然我最近没有尝试过,正如他所说,计划者已经有了改进。 这可能是第一件要尝试的事情。

否则,您必须重新排列分区以使用id值范围而不是现在使用的模数方法。 我知道,这不是一个好的解决方案。

另一种解决方案是将id的模数存储在单独的列中,然后可以对分区约束使用简单的值相等性检查。 但是,有点浪费磁盘空间,而且还需要在where子句中添加一个术语来启动。

除了Erwin关于规划器如何使用分区的细节的答案之外,这里还有一个更大的问题。

分区不是一个神奇的子弹。 有一些非常具体的事情,分区非常有用。 如果这些非常具体的事情都不适用于您,那么您不能指望通过分区提高性能,而且很可能会降低性能。

要正确进行分区,您需要全面了解您的使用模式或数据加载和卸载模式。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM