我面临一个奇怪的问题。当我正在开发我的应用程序的新功能时,我创建了生产数据库的转储,将它们加载到我的开发环境中。如果我尝试在开发和生产中运行相同的查询,我会得到 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