为什么Postgres进行顺序扫描,其中索引将返回< 1%的数据?

时间:2013-10-17 21:39:05

标签: sql postgresql database-tuning sql-tuning

我有19年的Oracle和MySQL经验(DBA和开发),我是Postgres的新手,所以我可能会遗漏一些明显的东西。但我不能让这个查询做我想做的事。

NOTE: This query is running on an EngineYard Postgres instance. I am not immediately aware of the parameters it has set up. Also, columns applicable_type and status in the items table are of extension type citext.

以下查询可能需要超过60秒才能返回行:

SELECT items.item_id, 
       CASE when items.sku is null then items.title else concat(item.title, ' (SKU: ', items.sku, ')') END title, 
       items.listing_status, items.updated_at, items.id, 
       items.sku, count(details.id) detail_count 
FROM "items" LEFT OUTER JOIN details ON details.applicable_id = items.id 
                                    and details.applicable_type = 'Item' 
                                    and details.status = 'Valid' 
                LEFT OUTER JOIN products ON products.id = items.product_id
WHERE "items"."user_id" = 3
GROUP BY items.id
ORDER BY title asc
LIMIT 25 OFFSET 0

details表包含6.5M行。它LEFT OUTER JOINapplicable_id进行顺序扫描。基数方面,该列在6.5M行中具有120K不同的可能性。

我在details上有一个btree索引,其中包含以下列:

applicable_id
applicable_type
status

但实际上,applicable_idapplicable_type基数较低。

我的explain analyze看起来像这样:

Limit  (cost=247701.59..247701.65 rows=25 width=118) (actual time=28781.090..28781.098 rows=25 loops=1)
  ->  Sort  (cost=247701.59..247703.05 rows=585 width=118) (actual time=28781.087..28781.090 rows=25 loops=1)
      Sort Key: (CASE WHEN (items.sku IS NULL) THEN (items.title)::text ELSE pg_catalog.concat(items.title, ' (SKU: ', items.sku, ')') END)
      Sort Method: top-N heapsort  Memory: 30kB
      ->  HashAggregate  (cost=247677.77..247685.08 rows=585 width=118) (actual time=28779.658..28779.974 rows=664 loops=1)
          ->  Hash Right Join  (cost=2069.47..247645.64 rows=6425 width=118) (actual time=17798.898..28742.395 rows=60047 loops=1)
                Hash Cond: (details.applicable_id = items.id)
                ->  Seq Scan on details  (cost=0.00..220591.65 rows=6645404 width=8) (actual time=6.272..27702.717 rows=6646205 loops=1)
                      Filter: ((applicable_type = 'Listing'::citext) AND (status = 'Valid'::citext))
                      Rows Removed by Filter: 942
                ->  Hash  (cost=2062.16..2062.16 rows=585 width=118) (actual time=1.286..1.286 rows=664 loops=1)
                      Buckets: 1024  Batches: 1  Memory Usage: 90kB
                      ->  Bitmap Heap Scan on items  (cost=16.87..2062.16 rows=585 width=118) (actual time=0.157..0.748 rows=664 loops=1)
                            Recheck Cond: (user_id = 3)
                            ->  Bitmap Index Scan on index_items_on_user_id  (cost=0.00..16.73 rows=585 width=0) (actual time=0.141..0.141 rows=664 loops=1)
                                  Index Cond: (user_id = 3)

总运行时间:28781.238 ms

2 个答案:

答案 0 :(得分:3)

你有一个表达式的索引产生标题吗?更好的是,一个(user_id,title_expression)。

如果没有,这可能是一个很好的添加,以便nestloop通过索引扫描的前25行,看到Postgres无法合理地猜测哪些随机25行(因此seq扫描你当前

。需要加入联名表

答案 1 :(得分:1)

我认为您只需要在applicable_id列上有一个索引(没有applicable_type,status列)。 您可能还需要增加default_statistics_target param(系统范围或更好,仅适用于applicable_id列),因此postgresql最好猜测加入时的行数。