相同的查询在生产中比开发慢1000倍

时间:2016-11-22 22:50:26

标签: postgresql

我有一个查询。在生产环境中,它需要大约15秒,而在开发中(使用生产数据库的几乎最新的快照),它不会超过100毫秒。

数据库并没有真正处于高负荷状态,每秒少于50次操作(可能更少)

生产:

 Nested Loop Anti Join  (cost=999.94..2116.89 rows=1 width=2171) (actual time=16922.305..16922.305 rows=0 loops=1)
   Join Filter: (auto_message_events.auto_message_id = a0.id)
   Rows Removed by Join Filter: 1683
   ->  Nested Loop Left Join  (cost=225.57..1311.15 rows=1 width=2235) (actual time=37.732..130.746 rows=5049 loops=1)
7.600..114.552 rows=51 loops=1)=202.12..329.72 rows=1 width=2235) (actual time=3--More--
               ->  Nested Loop  (cost=0.28..86.34 rows=3 width=2194) (actual time=0.905..1.088 rows=3 loops=1)
                     ->  Seq Scan on auto_messages a0  (cost=0.00..61.42 rows=3 width=2171) (actual time=0.880..1.009 rows=3 loops=1)
                           Filter: (((status)::text = 'live'::text) AND ((channel)::text = 'email'::text))
                           Rows Removed by Filter: 425
                     ->  Index Only Scan using apps_pkey on apps a1  (cost=0.28..8.29 rows=1 width=23) (actual time=0.014..0.017 rows=1 loops=3)
                           Index Cond: (id = (a0.app_id)::text)
                           Heap Fetches: 0
               ->  Hash Join  (cost=201.84..215.68 rows=1 width=313) (actual time=37.603..37.783 rows=17 loops=3)
                     Hash Cond: ((find_matching_visitors.id)::text = (v2.id)::text)
                     Join Filter: CASE WHEN a0.window_enabled THEN ((date_part('dow'::text, timezone(COALESCE((v2.location ->> 'timezone'::text), 'Europe/Paris'::text), now())) = ANY ((a0.window_days_of_week)::double precision[])) AND (timezone(COALESCE((v2.location ->> 'timezone'::text), 'Europe/Paris'::text), ('now'::cstring)::time with time zone) >= (a0.window_start)::time with time zone) AND (timezone(COALESCE((v2.location ->> 'timezone'::text), 'Europe/Paris'::text), ('now'::cstring)::time with time zone) <= (a0.window_end)::time with time zone)) ELSE true END
                     Rows Removed by Join Filter: 12
                     ->  Function Scan on find_matching_visitors  (cost=0.25..10.25 rows=1000 width=32) (actual time=29.358..29.393 rows=31 loops=3)
                     ->  Hash  (cost=201.54..201.54 rows=4 width=313) (actual time=8.037..8.037 rows=881 loops=3)
                           Buckets: 1024  Batches: 1  Memory Usage: 299kB
                           ->  Index Scan using visitors_app_id_signed_up_index on visitors v2  (cost=0.55..201.54 rows=4 width=313) (actual time=0.036..7.259 rows=881 loops=3)
                                 Index Cond: ((app_id)::text = (a1.id)::text)
                                 Filter: ((NOT merged) AND (email IS NOT NULL) AND (NOT unsubscribed) AND (((type)::text = 'user'::text) OR ((type)::text = 'lead'::text)))
                                 Rows Removed by Filter: 1558
         ->  Bitmap Heap Scan on auto_message_events a3  (cost=23.46..977.51 rows=392 width=4) (actual time=0.035..0.198 rows=99 loops=51)
               Recheck Cond: (auto_message_id = a0.id)
               Heap Blocks: exact=2499
               ->  Bitmap Index Scan on auto_message_events_auto_message_id_visitor_id_event_index  (cost=0.00..23.36 rows=392 width=0) (actual time=0.023..0.023 rows=99 loops=51)
                     Index Cond: (auto_message_id = a0.id)
h=73) (actual time=3.320..3.320 rows=1 loops=5049)ost=774.36..790.02 rows=6 widt--More--
         Recheck Cond: ((((visitor_id)::text = (v2.id)::text) AND ((event)::text = 'sent'::text)) OR (((event)::text = 'sent'::text) AND ((visitor_user_id)::text = (v2.user_id)::text)) OR (((event)::text = 'sent'::text) AND ((visitor_email)::text = (v2.email)::text)))
         Heap Blocks: exact=5940
         ->  BitmapOr  (cost=774.36..774.36 rows=6 width=0) (actual time=3.316..3.316 rows=0 loops=5049)
               ->  Bitmap Index Scan on auto_message_events_auto_message_id_visitor_id_event_index  (cost=0.00..746.48 rows=1 width=0) (actual time=3.088..3.088 rows=2 loops=5049)
                     Index Cond: (((visitor_id)::text = (v2.id)::text) AND ((event)::text = 'sent'::text))
               ->  Bitmap Index Scan on auto_message_id_event_visitor_user_id_idx  (cost=0.00..13.42 rows=3 width=0) (actual time=0.113..0.113 rows=2 loops=5049)
                     Index Cond: (((event)::text = 'sent'::text) AND ((visitor_user_id)::text = (v2.user_id)::text))
               ->  Bitmap Index Scan on auto_message_id_event_visitor_email_idx  (cost=0.00..14.46 rows=3 width=0) (actual time=0.110..0.110 rows=2 loops=5049)
                     Index Cond: (((event)::text = 'sent'::text) AND ((visitor_email)::text = (v2.email)::text))
 Planning time: 4.171 ms
 Execution time: 16922.671 ms

开发

QUERY PLAN
Nested Loop Anti Join  (cost=32.20..1602.94 rows=1 width=2175) (actual time=57.971..57.971 rows=0 loops=1)
  Join Filter: (((auto_message_events.visitor_id)::text = (v2.id)::text) OR ((auto_message_events.visitor_user_id)::text = (v2.user_id)::text) OR ((auto_message_events.visitor_email)::text = (v2.email)::text))
  Rows Removed by Join Filter: 4095
  ->  Nested Loop Left Join  (cost=16.29..595.98 rows=1 width=2736) (actual time=37.186..52.374 rows=91 loops=1)
        ->  Nested Loop  (cost=1.22..40.02 rows=1 width=2736) (actual time=37.134..52.245 rows=1 loops=1)
              Join Filter: ((v2.id)::text = (find_matching_visitors.id)::text)
              Rows Removed by Join Filter: 152
              ->  Nested Loop  (cost=0.97..17.27 rows=1 width=2736) (actual time=0.258..0.621 rows=3 loops=1)
                    Join Filter: CASE WHEN a0.window_enabled THEN ((date_part('dow'::text, timezone(COALESCE((v2.location ->> 'timezone'::text), 'Europe/Paris'::text), now())) = ANY ((a0.window_days_of_week)::double precision[])) AND (timezone(COALESCE((v2.location ->> 'timezone'::text), 'Europe/Paris'::text), ('now'::cstring)::time with time zone) >= (a0.window_start)::time with time zone) AND (timezone(COALESCE((v2.location ->> 'timezone'::text), 'Europe/Paris'::text), ('now'::cstring)::time with time zone) <= (a0.window_end)::time with time zone)) ELSE true END
                      Rows Removed by Join Filter: 6
                      ->  Nested Loop  (cost=0.70..16.75 rows=1 width=834) (actual time=0.105..0.196 rows=3 loops=1)
                            ->  Index Scan using email_idx on visitors v2  (cost=0.42..8.45 rows=1 width=810) (actual time=0.084..0.127 rows=3 loops=1)
                                  Index Cond: (email IS NOT NULL)
                                  Filter: ((NOT merged) AND (NOT unsubscribed) AND (((type)::text = 'user'::text) OR ((type)::text = 'lead'::text)))
                            ->  Index Only Scan using apps_pkey on apps a1  (cost=0.28..8.29 rows=1 width=24) (actual time=0.019..0.020 rows=1 loops=3)
                                  Index Cond: (id = (v2.app_id)::text)
                                  Heap Fetches: 3
                      ->  Index Scan using auto_messages_app_id_user_id_title_index on auto_messages a0  (cost=0.27..0.44 rows=1 width=2175) (actual time=0.057..0.100 rows=3 loops=3)
                            Index Cond: ((app_id)::text = (a1.id)::text)
                            Filter: (((status)::text = 'live'::text) AND ((channel)::text = 'email'::text))
                            Rows Removed by Filter: 49
                ->  Function Scan on find_matching_visitors  (cost=0.25..10.25 rows=1000 width=32) (actual time=17.191..17.195 rows=51 loops=3)
          ->  Bitmap Heap Scan on auto_message_events a3  (cost=15.07..552.54 rows=342 width=4) (actual time=0.043..0.080 rows=91 loops=1)
                Recheck Cond: (auto_message_id = a0.id)
                Heap Blocks: exact=26
                ->  Bitmap Index Scan on auto_message_events_auto_message_id_visitor_id_event_index  (cost=0.00..14.98 rows=342 width=0) (actual time=0.031..0.031 rows=91 loops=1)
                      Index Cond: (auto_message_id = a0.id)
    ->  Bitmap Heap Scan on auto_message_events  (cost=15.91..508.98 rows=281 width=73) (actual time=0.022..0.040 rows=46 loops=91)
          Recheck Cond: ((auto_message_id = a0.id) AND ((event)::text = 'sent'::text))
          Heap Blocks: exact=1820
          ->  Bitmap Index Scan on auto_message_events_auto_message_id_visitor_id_event_index  (cost=0.00..15.84 rows=281 width=0) (actual time=0.020..0.020 rows=52 loops=91)
                Index Cond: ((auto_message_id = a0.id) AND ((event)::text = 'sent'::text))
  Planning time: 4.882 ms
  Execution time: 58.174 ms

更新

加入时使用的动态查询功能:

CREATE OR REPLACE FUNCTION find_matching_visitors(app_id text, default_filters text[], custom_filters text[])
      RETURNS TABLE (
        id varchar
      ) AS
      $body$
      DECLARE
          default_filterstring text;
          custom_filterstring text;
          default_filter_length integer;
          custom_filter_length integer;
          sql VARCHAR;
      BEGIN
          default_filter_length := COALESCE(array_length(default_filters, 1), 0);
          custom_filter_length := COALESCE(array_length(custom_filters, 1), 0);

          default_filterstring := array_to_string(default_filters, ' AND ');
          custom_filterstring := array_to_string(custom_filters, ' AND ');

          IF custom_filterstring = '' or custom_filterstring is null THEN
              custom_filterstring := '1=1';
          END IF;

          IF default_filterstring = '' or default_filterstring is null THEN
              default_filterstring := '1=1';
          END IF;

          sql := format('
                      SELECT v.id FROM visitors v
                      LEFT JOIN trackings t on v.id = t.visitor_id
                      WHERE v.app_id = ''%s''
                      group by v.id
                      having (%s) AND (%s)
                  ', app_id, custom_filterstring, default_filterstring);
          RETURN QUERY EXECUTE sql;

      END;
      $body$
      LANGUAGE 'plpgsql';

有什么想法吗?

0 个答案:

没有答案