Postgres运行较慢的嵌套连接循环,而不是哈希连接

时间:2018-08-23 20:25:37

标签: sql postgresql indexing

我一直在针对生产数据库克隆优化一些sql查询。这是一个示例查询,其中我创建了两个索引,我们可以使用散列连接真正快速地运行仅索引扫描。

explain analyse
select activity.id from activity, notification
where notification.user_id = '9a51f675-e1e2-46e5-8bcd-6bc535c7e7cb'
and notification.received = false
and notification.invalid = false
and activity.id = notification.activity_id
and activity.space_id = 'e12b42ac-4e54-476f-a4f5-7d6bdb1e61e2'
order by activity.end_time desc
limit 21;

Limit  (cost=985.58..985.58 rows=1 width=24) (actual time=0.017..0.017 rows=0 loops=1)
  ->  Sort  (cost=985.58..985.58 rows=1 width=24) (actual time=0.016..0.016 rows=0 loops=1)
        Sort Key: activity.end_time DESC
        Sort Method: quicksort  Memory: 25kB
        ->  Hash Join  (cost=649.76..985.57 rows=1 width=24) (actual time=0.010..0.010 rows=0 loops=1)
              Hash Cond: (notification.activity_id = activity.id)
              ->  Index Only Scan using unreceived_notifications_index on notification  (cost=0.42..334.62 rows=127 width=16) (actual time=0.009..0.009 rows=0 loops=1)
                    Index Cond: (user_id = '9a51f675-e1e2-46e5-8bcd-6bc535c7e7cb'::uuid)
                    Heap Fetches: 0
              ->  Hash  (cost=634.00..634.00 rows=1227 width=24) (never executed)
                    ->  Index Only Scan using space_activity_index on activity  (cost=0.56..634.00 rows=1227 width=24) (never executed)
                          Index Cond: (space_id = 'e12b42ac-4e54-476f-a4f5-7d6bdb1e61e2'::uuid)
                          Heap Fetches: 0
Planning time: 0.299 ms
Execution time: 0.046 ms

这是索引。

create index unreceived_notifications_index on notification using btree (
    user_id,
    activity_id, -- index-only scan
    id -- index-only scan
) where (
    invalid = false
    and received = false
);

space_activity_index
create index space_activity_index on activity using btree (
    space_id,
    end_time desc,
    id -- index-only scan
);

但是,我注意到这些索引使我们的开发数据库变慢了很多。这是在我们的开发数据库中针对用户的相同查询,您会发现这次使用嵌套循环联接进行查询,并且循环的顺序确实效率低下。

explain analyse
select notification.id from notification, activity
where notification.user_id = '7c74a801-7cb5-4914-bbbe-2b18cd1ced76'
and notification.received = false
and notification.invalid = false
and activity.id = notification.activity_id
and activity.space_id = '415fc269-e68f-4da0-b3e3-b1273b741a7f'
order by activity.end_time desc
limit 20;

Limit  (cost=0.69..272.04 rows=20 width=24) (actual time=277.255..277.255 rows=0 loops=1)
  ->  Nested Loop  (cost=0.69..71487.55 rows=5269 width=24) (actual time=277.253..277.253 rows=0 loops=1)
        ->  Index Only Scan using space_activity_index on activity  (cost=0.42..15600.36 rows=155594 width=24) (actual time=0.016..59.433 rows=155666 loops=1)
              Index Cond: (space_id = '415fc269-e68f-4da0-b3e3-b1273b741a7f'::uuid)
              Heap Fetches: 38361
        ->  Index Only Scan using unreceived_notifications_index on notification  (cost=0.27..0.35 rows=1 width=32) (actual time=0.001..0.001 rows=0 loops=155666)
              Index Cond: ((user_id = '7c74a801-7cb5-4914-bbbe-2b18cd1ced76'::uuid) AND (activity_id = activity.id))
              Heap Fetches: 0
Planning time: 0.351 ms
Execution time: 277.286 ms

这里要注意的一件事是,我们的开发数据库中只有2个space_id。我怀疑这会导致Postgres变得更聪明,但实际上会使性能变差!

我的问题是:

  1. 是否可以通过某种方式强制Postgres运行哈希联接而不是嵌套循环联接?

  2. 一般来说,是否可以通过某种方式使Postgres的查询计划程序更具确定性?理想情况下,这些环境之间的查询性能特征应该完全相同。

谢谢。


编辑:请注意,当我在查询开发数据库时忽略了space_id条件时,结果会更快。

explain analyse
select notification.id from notification, activity
where notification.user_id = '7c74a801-7cb5-4914-bbbe-2b18cd1ced76'
and notification.received = false
and notification.invalid = false
and activity.id = notification.activity_id
--and activity.space_id = '415fc269-e68f-4da0-b3e3-b1273b741a7f'
order by activity.end_time desc
limit 20;

Limit  (cost=17628.13..17630.43 rows=20 width=24) (actual time=2.730..2.730 rows=0 loops=1)
  ->  Gather Merge  (cost=17628.13..17996.01 rows=3199 width=24) (actual time=2.729..2.729 rows=0 loops=1)
        Workers Planned: 1
        Workers Launched: 1
        ->  Sort  (cost=16628.12..16636.12 rows=3199 width=24) (actual time=0.126..0.126 rows=0 loops=2)
              Sort Key: activity.end_time DESC
              Sort Method: quicksort  Memory: 25kB
              ->  Nested Loop  (cost=20.59..16441.88 rows=3199 width=24) (actual time=0.093..0.093 rows=0 loops=2)
                    ->  Parallel Bitmap Heap Scan on notification  (cost=20.17..2512.17 rows=3199 width=32) (actual time=0.092..0.092 rows=0 loops=2)
                          Recheck Cond: ((user_id = '7c74a801-7cb5-4914-bbbe-2b18cd1ced76'::uuid) AND (NOT invalid) AND (NOT received))
                          ->  Bitmap Index Scan on unreceived_notifications_index  (cost=0.00..18.82 rows=5439 width=0) (actual time=0.006..0.006 rows=0 loops=1)
                                Index Cond: (user_id = '7c74a801-7cb5-4914-bbbe-2b18cd1ced76'::uuid)
                    ->  Index Scan using activity_pkey on activity  (cost=0.42..4.35 rows=1 width=24) (never executed)
                          Index Cond: (id = notification.activity_id)
Planning time: 0.344 ms
Execution time: 3.433 ms

编辑: reading about index hinting之后,我尝试使用set enable_nestloop=false;关闭nested_loop,查询速度更快!

Limit  (cost=20617.76..20620.09 rows=20 width=24) (actual time=2.872..2.872 rows=0 loops=1)
  ->  Gather Merge  (cost=20617.76..21130.20 rows=4392 width=24) (actual time=2.871..2.871 rows=0 loops=1)
        Workers Planned: 2
        Workers Launched: 2
        ->  Sort  (cost=19617.74..19623.23 rows=2196 width=24) (actual time=0.086..0.086 rows=0 loops=3)
              Sort Key: activity.end_time DESC
              Sort Method: quicksort  Memory: 25kB
              ->  Hash Join  (cost=2609.20..19495.85 rows=2196 width=24) (actual time=0.062..0.062 rows=0 loops=3)
                    Hash Cond: (activity.id = notification.activity_id)
                    ->  Parallel Seq Scan on activity  (cost=0.00..14514.57 rows=64831 width=24) (actual time=0.006..0.006 rows=1 loops=3)
                          Filter: (space_id = '415fc269-e68f-4da0-b3e3-b1273b741a7f'::uuid)
                    ->  Hash  (cost=2541.19..2541.19 rows=5441 width=32) (actual time=0.007..0.007 rows=0 loops=3)
                          Buckets: 8192  Batches: 1  Memory Usage: 64kB
                          ->  Bitmap Heap Scan on notification  (cost=20.18..2541.19 rows=5441 width=32) (actual time=0.006..0.006 rows=0 loops=3)
                                Recheck Cond: ((user_id = '7c74a801-7cb5-4914-bbbe-2b18cd1ced76'::uuid) AND (NOT invalid) AND (NOT received))
                                ->  Bitmap Index Scan on unreceived_notifications_index  (cost=0.00..18.82 rows=5441 width=0) (actual time=0.004..0.004 rows=0 loops=3)
                                      Index Cond: (user_id = '7c74a801-7cb5-4914-bbbe-2b18cd1ced76'::uuid)
Planning time: 0.375 ms
Execution time: 3.630 ms

0 个答案:

没有答案