0

我面临一个奇怪的问题。当我正在开发我的应用程序的新功能时,我创建了生产数据库的转储,将它们加载到我的开发环境中。如果我尝试在开发和生产中运行相同的查询,我会得到 2 个不同的计划,返回非常不同的查询时间:

查询如下:

 SELECT     film_occupations.id,
           Upper(theaters.NAME) AS theater_name,
           Upper(films.title)   AS film_title,
           film_occupations.start,
           To_char(film_occupations.start at time zone 'UTC' at time zone 'Europe/Rome',    'HH24MI')                                                   AS projections_day,
           count_seats.seats_count - COALESCE(count_tickets.counter, 0) - COALESCE(count_reservations.counter, 0) - COALESCE(count_locks.counter,     0) AS available_seats
FROM       "film_occupations"
INNER JOIN "theaters"
ON         "theaters"."id" = "film_occupations"."theater_id"
INNER JOIN "films"
ON         "films"."id" = "film_occupations"."film_id"
LEFT JOIN
           (
                  SELECT seats_count,
                         id
                  FROM   theaters) AS count_seats
ON         count_seats.id = film_occupations.theater_id
LEFT JOIN
           (
                    SELECT   Count(id) AS counter,
                             film_occupation_id
                    FROM     film_tickets
                    WHERE    film_tickets.void = 'f'
                    GROUP BY film_occupation_id) AS count_tickets
ON         count_tickets.film_occupation_id = film_occupations.id
LEFT JOIN
           (
                    SELECT   Count(id) AS counter,
                             film_occupation_id
                    FROM     film_reservations
                    WHERE    film_reservations.archived = 'f'
                    AND      film_reservations.film_ticket_id IS NULL
                    GROUP BY film_occupation_id) AS count_reservations
ON         count_reservations.film_occupation_id = film_occupations.id
LEFT JOIN
           (
                    SELECT   Count(id) AS counter,
                             film_occupation_id
                    FROM     seat_locks
                    WHERE    seat_locks.persistent = 't'
                    GROUP BY film_occupation_id) AS count_locks
ON         count_locks.film_occupation_id = film_occupations.id
WHERE      "film_occupations"."subsystem_id" IN (1,
                                                 3)
AND        "theaters"."cinema_id" = 7
AND        "film_occupations"."hidden" = 'f'
AND        ((
                                 NOT disable_online)
           OR         (
                                 NOT disable_counter))
AND        (
                      film_occupations.start > '2016-08-09 11:02:45.010316'
           AND        film_occupations.start < '2016-08-10 02:59:59.999999')
AND        ((
                                 film_occupations.disable_online IS NULL
                      OR         film_occupations.disable_online = 'f')
           OR         (
                                 film_occupations.disable_online_booking IS NULL
                      OR         film_occupations.disable_online_booking = 'f')
           OR         (
                                 film_occupations.disable_counter IS NULL
                      OR         film_occupations.disable_counter = 'f')
           OR         (
                                 online_visibility_expiration >= '2016-08-09'
                      OR         online_visibility_expiration IS NULL))
GROUP BY   film_occupations.id,
           theater_name,
           film_title,
           film_occupations.start,
           projections_day,
           theaters.seats_count,
           count_seats.seats_count,
           count_tickets.counter,
           count_reservations.counter,
           count_locks.counter
ORDER BY   theater_name,
           film_title,
           film_occupations.start ASC,
           film_title 

它在开发中启动时生成以下计划:

  QUERY PLAN
  --------------------------------------------------------------------------------------------------------------------------------------------------    --------------------------------------------------------------------------------------------------------------------------------------------------    --------------------------------------------------------------------------------------------------------------------------------------------------      ------------------------------------------------------------------------      -----------------------------------------------------------------------------------------------------------------
   Group  (cost=41835.73..41835.78 rows=1 width=583)
     Group Key: (upper((theaters.name)::text)), (upper((films.title)::text)), film_occupations.start, film_occupations.id, (to_char(timezone('Europe    /Rome'::text, (film_occupations.start)::timestamp with time zone), 'HH24MI'::text)), theaters.seats_count, theaters_1.seats_count, (count(   film_tickets.id)), (count(film_reservations.id)), (count(seat_locks.id))
     ->  Sort  (cost=41835.73..41835.73 rows=1 width=583)
           Sort Key: (upper((theaters.name)::text)), (upper((films.title)::text)), film_occupations.start, film_occupations.id, (to_char(      timezone('Europe/Rome'::text, (film_occupations.start)::timestamp with time zone), 'HH24MI'::text)), theaters.seats_count,       theaters_1.seats_count, (count(film_tickets.id)), (count(film_reservations.id)), (count(seat_locks.id))
           ->  Nested Loop Left Join  (cost=40790.60..41835.72 rows=1 width=583)
                 Join Filter: (seat_locks.film_occupation_id = film_occupations.id)
                 ->  Nested Loop Left Join  (cost=40782.32..41826.56 rows=1 width=575)
                       Join Filter: (film_reservations.film_occupation_id = film_occupations.id)
                       ->  Nested Loop Left Join  (cost=40325.03..41369.14 rows=1 width=567)
                             Join Filter: (film_tickets.film_occupation_id = film_occupations.id)
                             ->  Nested Loop Left Join  (cost=19.99..544.35 rows=1 width=559)
                                   Join Filter: (theaters_1.id = film_occupations.theater_id)
                                   ->  Nested Loop  (cost=19.99..542.68 rows=1 width=559)
                                         ->  Nested Loop  (cost=19.71..534.37 rows=1 width=540)
                                               ->  Seq Scan on theaters  (cost=0.00..1.38 rows=1 width=524)
                                                     Filter: (cinema_id = 7)
                                               ->  Bitmap Heap Scan on film_occupations  (cost=19.71..532.99 rows=1 width=20)
                                                     Recheck Cond: (theater_id = theaters.id)
                                                     Filter: ((NOT hidden) AND ((NOT disable_online) OR (NOT disable_counter)) AND (subsystem_id =      ANY ('{1,3}'::integer[])) AND (start > '2016-08-09 11:02:45.010316'::timestamp without time     zone) AND (start < '2016-08-10 02:59:59.999999'::timestamp without time zone) AND ((    disable_online IS NULL) OR (NOT disable_online) OR (disable_online_booking IS NULL) OR (NOT   disable_online_booking) OR (disable_counter IS NULL) OR (NOT disable_counter) OR (      online_visibility_expiration >= '2016-08-09 00:00:00'::timestamp without time zone) OR (     online_visibility_expiration IS NULL)))
                                                     ->  Bitmap Index Scan on index_film_occupations_on_theater_id  (cost=0.00..19.71 rows=990       width=0)
                                                           Index Cond: (theater_id = theaters.id)
                                         ->  Index Scan using films_pkey on films  (cost=0.28..8.29 rows=1 width=27)
                                               Index Cond: (id = film_occupations.film_id)
                                   ->  Seq Scan on theaters theaters_1  (cost=0.00..1.30 rows=30 width=8)
                             ->  HashAggregate  (cost=40305.04..40464.96 rows=15992 width=8)
                                   Group Key: film_tickets.film_occupation_id
                                   ->  Seq Scan on film_tickets  (cost=0.00..36098.88 rows=841233 width=8)
                                         Filter: (NOT void)
                       ->  HashAggregate  (cost=457.29..457.33 rows=4 width=8)
                             Group Key: film_reservations.film_occupation_id
                             ->  Seq Scan on film_reservations  (cost=0.00..457.17 rows=24 width=8)
                                   Filter: ((NOT archived) AND (film_ticket_id IS NULL))
                 ->  HashAggregate  (cost=8.28..8.54 rows=27 width=8)
                       Group Key: seat_locks.film_occupation_id
                       ->  Seq Scan on seat_locks  (cost=0.00..6.85 rows=285 width=8)
                             Filter: persistent
  (36 rows)

总运行时间始终低于 500 毫秒,而它计划这样做:

QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------   -------------------------------------------------------------------------------------------------------------------------------------------------    -------------------------------------------------------------------------------------------------------------------------------------------------   -----------------------------------------------------------------------    ----------------------------------------------------------------------------------------------------------------------------------
Group  (cost=149898.67..149898.72 rows=1 width=73)
  ->  Sort  (cost=149898.67..149898.67 rows=1 width=73)
        Sort Key: (upper((theaters.name)::text)), (upper((films.title)::text)), film_occupations.start, film_occupations.id, (to_char(timezone('Europe/Rome'::text, timezone('UTC'::text, film_occupations.start)), 'HH24MI'::text)), theaters.seats_count, theaters_1.seats_count, (count(film_tickets.id)), (count(film_reservations.id)), (count(seat_locks.id))
        ->  Merge Right Join  (cost=143225.72..149898.66 rows=1 width=73)
              Merge Cond: (film_tickets.film_occupation_id = film_occupations.id)
              ->  GroupAggregate  (cost=141806.56..148269.62 rows=15939 width=8)
                    ->  Sort  (cost=141806.56..143907.78 rows=840489 width=8)
                          Sort Key: film_tickets.film_occupation_id
                          ->  Seq Scan on film_tickets  (cost=0.00..36110.79 rows=840489 width=8)
                                Filter: (NOT void)
              ->  Materialize  (cost=1419.16..1429.77 rows=1 width=65)
                    ->  Nested Loop Left Join  (cost=1419.16..1429.77 rows=1 width=65)
                          Join Filter: (seat_locks.film_occupation_id = film_occupations.id)
                          ->  Nested Loop Left Join  (cost=1410.88..1420.62 rows=1 width=57)
                                Join Filter: (theaters_1.id = film_occupations.theater_id)
                                ->  Nested Loop  (cost=1410.88..1418.94 rows=1 width=57)
                                      ->  Merge Left Join  (cost=1410.61..1410.64 rows=1 width=38)
                                            Merge Cond: (film_occupations.id = film_reservations.film_occupation_id)
                                            ->  Sort  (cost=953.39..953.40 rows=1 width=30)
                                                  Sort Key: film_occupations.id
                                                  ->  Merge Join  (cost=953.34..953.38 rows=1 width=30)
                                                        Merge Cond: (film_occupations.theater_id = theaters.id)
                                                        ->  Sort  (cost=951.91..951.92 rows=2 width=20)
                                                              Sort Key: film_occupations.theater_id
                                                              ->  Seq Scan on film_occupations  (cost=0.00..951.90 rows=2 width=20)
                                                                    Filter: ((NOT hidden) AND ((NOT disable_online) OR (NOT disable_counter)) AND (subsystem_id = ANY ('{1,3}'::integer[])) AND (start > '2016-08-09 11:24:23.934692'::timestamp without time zone) AND (start < '2016-08-10 02:59:59.999999'::timestamp without time zone) AND ((disable_online IS NULL) OR (NOT disable_online) OR (disable_online_booking IS NULL) OR (NOT disable_online_booking) OR (disable_counter IS NULL) OR (NOT disable_counter) OR (online_visibility_expiration >= '2016-08-09 00:00:00'::timestamp without time zone) OR (online_visibility_expiration IS NULL)))
                                                        ->  Sort  (cost=1.43..1.45 rows=5 width=14)
                                                              Sort Key: theaters.id
                                                              ->  Seq Scan on theaters  (cost=0.00..1.38 rows=5 width=14)
                                                                    Filter: (cinema_id = 7)
                                            ->  Sort  (cost=457.21..457.22 rows=3 width=12)
                                                  Sort Key: film_reservations.film_occupation_id
                                                  ->  HashAggregate  (cost=457.13..457.16 rows=3 width=8)
                                                        ->  Seq Scan on film_reservations  (cost=0.00..457.04 rows=18 width=8)
                                                              Filter: ((NOT archived) AND (film_ticket_id IS NULL))
                                      ->  Index Scan using films_pkey on films  (cost=0.28..8.29 rows=1 width=27)
                                            Index Cond: (id = film_occupations.film_id)
                                ->  Seq Scan on theaters theaters_1  (cost=0.00..1.30 rows=30 width=8)
                          ->  HashAggregate  (cost=8.28..8.54 rows=27 width=8)
                                ->  Seq Scan on seat_locks  (cost=0.00..6.85 rows=285 width=8)
                                      Filter: persistent
41 rows)

在生产中运行时间超过 1.5 秒。请考虑生产数据库是定期清理的(我刚刚对真空进行了详细分析;同样,但没有任何改变)。

我认为首先出错的是成本估算(生产中的 149898.67 与开发中的 41835.73)。我如何改进生产中的规划器?

先感谢您!

阿尔贝托

更新 here你可以找到2个postgres.conf之间的差异。请考虑发现的所有差异都已注释,因此我认为两个 pg 都使用默认值(我认为它们是注释的值。还请注意 dev 正在运行 v9.5.3 而 prod 正在运行 v9.3.4

value                 production development
work_mem              1MB        4MB
maintenance_work_mem  16MB       64MB
max_replication_slots missing    0
effective_cache_size  128MB      4GB
4

0 回答 0