Postgres优化器为外连接选择错误的执行计划

时间:2016-02-24 15:32:16

标签: postgresql-9.3

我有以下数据模型:

包含~310M行的父表:

Table parent:
  Column    | Type
------------+-------------------------------
id          | BIGINT (Primary key, sequence)
type        | VARCHAR
group       | VARCHAR
date        | TIMESTAMP
isok        | BOOLEAN

With an index on (group,isok) where isok = false

和一个〜1000M行的孩子:

Table child
  Column    | Type
------------+-------------------------------
parentid    | BIGINT (Foreign Key)
field1      | VARCHAR
field2      | VARCHAR

With an index on (parentid)

1父母可以有0到N个孩子。

我需要执行此查询:

SELECT p.id, p.type, p.date, c.field1, c.field2 
FROM parent p
LEFT OUTER JOIN child AS c ON p.id = c.parentid
WHERE group = 'groupname' AND isok = false;

EXPLAIN ANALYZE告诉我查询计划是:

                                                                         QUERY PLAN                                                                          
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Hash Right Join  (cost=223072.57..34724441.40 rows=698363 width=65) (actual time=7944.249..933430.677 rows=286257 loops=1)
  Hash Cond: (c.parentid = p.id)
  ->  Seq Scan on child c  (cost=0.00..23840617.04 rows=1217573504 width=47) (actual time=0.005..488678.149 rows=1217573499 loops=1)
  ->  Hash  (cost=220871.38..220871.38 rows=176095 width=26) (actual time=206.169..206.169 rows=283686 loops=1)
        Buckets: 32768  Batches: 1  Memory Usage: 17731kB
        ->  Index Scan using parent_group_nok_idx on parent p  (cost=0.55..220871.38 rows=176095 width=26) (actual time=0.032..115.183 rows=283686 loops=1)
              Index Cond: (((group)::text = 'groupname'::text) AND (isok = false))
Total runtime: 933486.035 ms

当我禁用seqscans时:

                                                                   QUERY PLAN                                                                       
--------------------------------------------------------------------------------------------------------------------------------------------------------
 Nested Loop Left Join  (cost=1.13..35309490.28 rows=698363 width=65) (actual time=0.684..42144.558 rows=286257 loops=1)
   ->  Index Scan using parent_group_nok_idx on parent p  (cost=0.55..220871.38 rows=176095 width=26) (actual time=0.030..122.959 rows=283686 loops=1)
         Index Cond: (((group)::text = 'groupname'::text) AND (isok = false))
   ->  Index Scan using child_parentid_idx on child c  (cost=0.58..184.74 rows=1452 width=47) (actual time=0.145..0.147 rows=1 loops=283686)
         Index Cond: (parentid = p.id)
 Total runtime: 42200.478 ms

我可以做什么(禁用seq扫描除外)“强制”优化器选择索引方式?

1 个答案:

答案 0 :(得分:3)

在查找类似问题并阅读本文后:trumping-the-postgresql-query-planner,我尝试使用CTE查询:这是我使用的查询:

WITH cte AS (
    SELECT id, type, date 
    FROM parent 
    WHERE group = 'groupname' AND isok = false 
    ORDER BY id ASC
)
SELECT cte.id, cte.type, cte.date, c.field1, c.field2 
FROM cte LEFT OUTER JOIN child c ON c.parentid = cte.id;

现在,有查询计划:

                                                                           QUERY PLAN                                                                           
----------------------------------------------------------------------------------------------------------------------------------------------------------------
 Nested Loop Left Join  (cost=236655.35..33098268.28 rows=238777013 width=56) (actual time=243.160..1473.618 rows=286257 loops=1)
   CTE cte
     ->  Sort  (cost=236214.54..236654.77 rows=176095 width=26) (actual time=243.135..314.067 rows=283686 loops=1)
           Sort Key: e.id
           Sort Method: quicksort  Memory: 34451kB
           ->  Index Scan using parent_group_nok_idx on parent  (cost=0.55..220871.38 rows=176095 width=26) (actual time=0.041..113.058 rows=283686 loops=1)
                 Index Cond: (((group)::text = 'groupname'::text) AND (isok = false))
   ->  CTE Scan on cte  (cost=0.00..3521.90 rows=176095 width=18) (actual time=243.140..449.385 rows=283686 loops=1)
   ->  Index Scan using child_parentid_idx on child c  (cost=0.58..173.03 rows=1356 width=46) (actual time=0.002..0.003 rows=1 loops=283686)
         Index Cond: (parentid = cte.id)
 Total runtime: 1526.945 ms

现在使用我的索引。