完全相同的查询和数据库。在生产中慢了100多倍

时间:2016-12-12 22:04:46

标签: postgresql database-performance sql-execution-plan

我在生产(Linux)和开发(Mac)

中运行PG 9.6.1

我正在开发和生产中测试相同的查询。在开发过程中,我测试了生产数据库的多个快照(仅相隔几分钟)。

出于某种原因,同样的查询提供了截然不同的查询计划,导致生产的计划性能很差。

当我执行以下查询时

SELECT DISTINCT ON (v0."email") v0."id"
  ,v0."first_seen" ,v0."last_seen" ,v0."last_heard_from" ,v0."last_contacted" ,v0."last_page_viewed" ,v0."sessions" ,v0."seen_welcome_message" ,v0."signed_up_at" ,v0."referrer" ,v0."unsubscribed" ,v0."merged" ,v0."marked_at" ,v0."first_name" ,v0."last_name" ,v0."email" ,v0."slug" ,v0."name" ,v0."type" ,v0."avatar" ,v0."user_id" ,v0."location" ,v0."app_id" ,v0."actor_id" ,v0."marked_by_id" ,v0."inserted_at" ,v0."updated_at"
FROM "visitors" AS v0
INNER JOIN "auto_messages" AS a1 ON (a1."app_id" = v0."app_id")
  AND (a1."id" = $1)
INNER JOIN LATERAL(SELECT * FROM find_matching_visitors(a1."app_id", a1."formatted_default_filters", a1."formatted_custom_filters")) AS f2 ON f2."id" = v0."id"
WHERE (
    CASE 
      WHEN a1."window_enabled"
        THEN extract(dow FROM current_timestamp at TIME zone COALESCE((v0."location" - >> 'timezone'), 'Europe/Paris')) = ANY (a1."window_days_of_week"::INT [])
          AND CURRENT_TIME at TIME zone COALESCE((v0."location" - >> 'timezone'), 'Europe/Paris') BETWEEN a1."window_start"
            AND a1."window_end"
      ELSE true
      END
    )
  AND (
    NOT EXISTS (
      SELECT *
      FROM auto_message_events
      WHERE auto_message_id = a1."id"
        AND (
          visitor_id = v0."id"
          OR visitor_user_id = v0."user_id"
          OR visitor_email = v0."email"
          )
        AND event = 'sent'
      )
    )
  AND ((v0."type" = 'user') OR (v0."type" = 'lead'))
  AND (NOT (v0."merged"))
  AND (v0."marked_at" IS NULL)
  AND (NOT (v0."email" IS NULL) AND NOT (v0."unsubscribed"))
  AND ((a1."status" = $2) AND (a1."channel" = $3))
GROUP BY v0."id"
ORDER BY v0."email"
  ,v0."last_seen" DESC LIMIT $4 [446, "live", "email", 500]

制作中,我获得以下查询计划

 Limit  (cost=1085.72..1085.73 rows=1 width=1070) (actual time=16862.966..16862.966 rows=0 loops=1)
.16862.964 rows=0 loops=1)72..1085.73 rows=1 width=1070) (actual time=16862.964.--More--
         ->  Sort  (cost=1085.72..1085.73 rows=1 width=1070) (actual time=16862.963..16862.963 rows=0 loops=1)
               Sort Key: v0.email, v0.last_seen DESC
               Sort Method: quicksort  Memory: 25kB
               ->  Group  (cost=1085.71..1085.71 rows=1 width=1070) (actual time=16862.946..16862.946 rows=0 loops=1)
                     Group Key: v0.id
                     ->  Sort  (cost=1085.71..1085.71 rows=1 width=1070) (actual time=16862.936..16862.936 rows=0 loops=1)
                           Sort Key: v0.id
                           Sort Method: quicksort  Memory: 25kB
                           ->  Nested Loop Anti Join  (cost=890.31..1085.70 rows=1 width=1070) (actual time=16862.922..16862.922 rows=0 loops=1)
                                 Join Filter: (auto_message_events.auto_message_id = a1.id)
                                 ->  Nested Loop  (cost=885.54..1072.87 rows=1 width=1074) (actual time=46.013..16856.367 rows=107 loops=1)
                                       Join Filter: ((v0.id)::text = (find_matching_visitors.id)::text)
                                       Rows Removed by Join Filter: 112824
                                       ->  Nested Loop  (cost=885.29..1050.12 rows=1 width=1162) (actual time=4.020..14.710 rows=949 loops=1)
                                             ->  Index Scan using auto_messages_pkey on auto_messages a1  (cost=0.27..8.30 rows=1 width=122) (actual time=0.019..0.021 rows=1 loops=1)
                                                   Index Cond: (id = 435)
                                                   Filter: (((status)::text = 'live'::text) AND ((channel)::text = 'email'::text))
                                             ->  Bitmap Heap Scan on visitors v0  (cost=885.02..1041.81 rows=1 width=1070) (actual time=3.995..13.180 rows=949 loops=1)
                                                   Recheck Cond: ((email IS NOT NULL) AND ((app_id)::text = (a1.app_id)::text))
                                                   Filter: ((NOT merged) AND (marked_at IS NULL) AND (NOT unsubscribed) AND (((type)::text = 'user'::text) OR ((type)::text = 'lead'::text)) AND CASE WHEN a1.window_enabled THEN ((date_part('dow'::text, timezone(COALESCE((location ->> 'timezone'::text), 'Europe/Paris'::text), now())) = ANY ((a1.window_days_of_week)::double precision[])) AND (timezone(COALESCE((location ->> 'timezone'::text), 'Europe/Paris'::text), ('now'::cstring)::time with time zone) >= (a1.window_start)::time with time zone) AND (timezone(COALESCE((location ->> 'timezone'::text), 'Europe/Paris'::text), ('now'::cstring)::time with time zone) <= (a1.window_end)::time with time zone)) ELSE true END)
                                                   Rows Removed by Filter: 56
                                                   Heap Blocks: exact=624
                                                   ->  BitmapAnd  (cost=885.02..885.02 rows=39 width=0) (actual time=3.874..3.874 rows=0 loops=1)
                                                         ->  Bitmap Index Scan on email_idx  (cost=0.00..284.01 rows=6344 width=0) (actual time=2.161..2.161 rows=9749 loops=1)
                                                               Index Cond: (email IS NOT NULL)
                                                         ->  Bitmap Index Scan on visitors_app_id_signed_up_index  (cost=0.00..600.68 rows=6950 width=0) (actual time=1.086..1.086 rows=4502 loops=1)
                                                               Index Cond: ((app_id)::text = (a1.app_id)::text)
                                       ->  Function Scan on find_matching_visitors  (cost=0.25..10.25 rows=1000 width=32) (actual time=17.681..17.706 rows=119 loops=949)
                                 ->  Bitmap Heap Scan on auto_message_events  (cost=4.77..8.79 rows=1 width=74) (actual time=0.051..0.051 rows=1 loops=107)
                                       Recheck Cond: (((auto_message_id = 435) AND ((visitor_id)::text = (v0.id)::text) AND ((event)::text = 'sent'::text)) OR ((auto_message_id = 435) AND ((event)::text = 'sent'::text) AND ((visitor_user_id)::text = (v0.user_id)::text)) OR ((auto_message_id = 435) AND ((event)::text = 'sent'::text) AND ((visitor_email)::text = (v0.email)::text)))
                                       Heap Blocks: exact=107
                                       ->  BitmapOr  (cost=4.77..4.77 rows=1 width=0) (actual time=0.044..0.044 rows=0 loops=107)
                                             ->  Bitmap Index Scan on auto_message_events_auto_message_id_visitor_id_event_index  (cost=0.00..3.56 rows=1 width=0) (actual time=0.019..0.019 rows=1 loops=107)
                                                   Index Cond: ((auto_message_id = 435) AND ((visitor_id)::text = (v0.id)::text) AND ((event)::text = 'sent'::text))
                                             ->  Bitmap Index Scan on auto_message_id_event_visitor_user_id_idx  (cost=0.00..0.59 rows=1 width=0) (actual time=0.008..0.008 rows=1 loops=107)
                                                   Index Cond: ((auto_message_id = 435) AND ((event)::text = 'sent'::text) AND ((visitor_user_id)::text = (v0.user_id)::text))
                                             ->  Bitmap Index Scan on auto_message_id_event_visitor_email_idx  (cost=0.00..0.62 rows=1 width=0) (actual time=0.008..0.008 rows=1 loops=107)
                                                   Index Cond: ((auto_message_id = 435) AND ((event)::text = 'sent'::text) AND ((visitor_email)::text = (v0.email)::text))
 Planning time: 2.566 ms
 Execution time: 16863.358 ms

开发(生产数据库快照)

中运行的完全相同的查询
    Limit  (cost=417.94..417.95 rows=1 width=2096) (actual time=90.445..90.445 rows=0 loops=1)
   ->  Unique  (cost=417.94..417.95 rows=1 width=2096) (actual time=90.444..90.444 rows=0 loops=1)
         ->  Sort  (cost=417.94..417.95 rows=1 width=2096) (actual time=90.443..90.443 rows=0 loops=1)
               Sort Key: v0.email, v0.last_seen DESC
               Sort Method: quicksort  Memory: 25kB
               ->  Group  (cost=417.92..417.93 rows=1 width=2096) (actual time=90.435..90.435 rows=0 loops=1)
                     Group Key: v0.id
                     ->  Sort  (cost=417.92..417.93 rows=1 width=2096) (actual time=90.435..90.435 rows=0 loops=1)
                           Sort Key: v0.id
                            Sort Method: quicksort  Memory: 25kB
                            ->  Nested Loop Anti Join  (cost=388.06..417.91 rows=1 width=2096) (actual time=90.430..90.430 rows=0 loops=1)
                                  Join Filter: (auto_message_events.auto_message_id = a1.id)
                                  ->  Nested Loop  (cost=375.05..396.85 rows=1 width=2100) (actual time=87.181..87.341 rows=76 loops=1)
                                        ->  Index Scan using auto_messages_pkey on auto_messages a1  (cost=0.28..8.30 rows=1 width=120) (actual time=0.015..0.017 rows=1 loops=1)
                                              Index Cond: (id = 435)
                                              Filter: (((status)::text = 'live'::text) AND ((channel)::text = 'email'::text))
                                        ->  Hash Join  (cost=374.78..388.54 rows=1 width=2096) (actual time=87.163..87.292 rows=76 loops=1)
                                              Hash Cond: ((find_matching_visitors.id)::text = (v0.id)::text)
                                              ->  Function Scan on find_matching_visitors  (cost=0.25..10.25 rows=1000 width=32) (actual time=79.139..79.151 rows=81 loops=1)
                                              ->  Hash  (cost=374.52..374.52 rows=1 width=2096) (actual time=8.008..8.008 rows=911 loops=1)
                                                    Buckets: 1024  Batches: 1  Memory Usage: 468kB
                                                    ->  Bitmap Heap Scan on visitors v0  (cost=230.95..374.52 rows=1 width=2096) (actual time=5.024..6.986 rows=911 loops=1)
                                                          Recheck Cond: (((app_id)::text = (a1.app_id)::text) AND (email IS NOT NULL))
                                                          Filter: ((NOT merged) AND (marked_at IS NULL) AND (NOT unsubscribed) AND (((type)::text = 'user'::text) OR ((type)::text = 'lead'::text)) AND CASE WHEN a1.window_enabled THEN ((date_part('dow'::text, timezone(COALESCE((location ->> 'timezone'::text), 'Europe/Paris'::text), now())) = ANY ((a1.window_days_of_week)::double precision[])) AND (timezone(COALESCE((location ->> 'timezone'::text), 'Europe/Paris'::text), ('now'::cstring)::time with time zone) >= (a1.window_start)::time with time zone) AND (timezone(COALESCE((location ->> 'timezone'::text), 'Europe/Paris'::text), ('now'::cstring)::time with time zone) <= (a1.window_end)::time with time zone)) ELSE true END)
                                                          Rows Removed by Filter: 43
                                                          Heap Blocks: exact=1094
                                                          ->  BitmapAnd  (cost=230.95..230.95 rows=36 width=0) (actual time=4.804..4.804 rows=0 loops=1)
                                                                ->  Bitmap Index Scan on visitors_app_id_signed_up_index  (cost=0.00..101.44 rows=2802 width=0) (actual time=1.044..1.044 rows=4670 loops=1)
                                                                      Index Cond: ((app_id)::text = (a1.app_id)::text)
                                                                ->  Bitmap Index Scan on email_idx  (cost=0.00..129.26 rows=6512 width=0) (actual time=3.313..3.313 rows=20201 loops=1)
                                                                      Index Cond: (email IS NOT NULL)
                                  ->  Bitmap Heap Scan on auto_message_events  (cost=13.01..17.03 rows=1 width=73) (actual time=0.039..0.039 rows=1 loops=76)
                                        Recheck Cond: (((auto_message_id = 435) AND ((visitor_id)::text = (v0.id)::text) AND ((event)::text = 'sent'::text)) OR ((auto_message_id = 435) AND ((event)::text = 'sent'::text) AND ((visitor_user_id)::text = (v0.user_id)::text)) OR ((auto_message_id = 435) AND ((event)::text = 'sent'::text) AND ((visitor_email)::text = (v0.email)::text)))
                                        Heap Blocks: exact=76
                                        ->  BitmapOr  (cost=13.01..13.01 rows=1 width=0) (actual time=0.037..0.037 rows=0 loops=76)
                                              ->  Bitmap Index Scan on auto_message_events_auto_message_id_visitor_id_event_index  (cost=0.00..4.43 rows=1 width=0) (actual time=0.011..0.011 rows=1 loops=76)
                                                    Index Cond: ((auto_message_id = 435) AND ((visitor_id)::text = (v0.id)::text) AND ((event)::text = 'sent'::text))
                                              ->  Bitmap Index Scan on auto_message_id_event_visitor_user_id_idx  (cost=0.00..4.29 rows=1 width=0) (actual time=0.011..0.011 rows=1 loops=76)
                                                    Index Cond: ((auto_message_id = 435) AND ((event)::text = 'sent'::text) AND ((visitor_user_id)::text = (v0.user_id)::text))
                                              ->  Bitmap Index Scan on auto_message_id_event_visitor_email_idx  (cost=0.00..4.29 rows=1 width=0) (actual time=0.014..0.014 rows=0 loops=76)
                                                    Index Cond: ((auto_message_id = 435) AND ((event)::text = 'sent'::text) AND ((visitor_email)::text = (v0.email)::text))
  Planning time: 2.848 ms
  Execution time: 90.722 ms

更新

事实证明,从生产中的查询中删除表达式(NOT (v0."email" IS NULL),性能会自行纠正。

为什么有任何想法? (该字段已编入索引)

0 个答案:

没有答案