Медленная оптимизация запросов в Postgres - PullRequest
6 голосов
/ 04 июня 2019

У нас есть проблема производительности с конкретным запросом SQL, и мы пытаемся выяснить, как мы могли бы здесь улучшить.Время его выполнения составляет около 20 - 100 секунд!

Вот запрос и объяснение:

SELECT  "jobs".* FROM "jobs"
  WHERE "jobs"."status" IN (1, 2, 3, 4)
  ORDER BY "jobs"."due_date" ASC
  LIMIT 5;


Limit  (cost=0.42..1844.98 rows=5 width=2642) (actual time=16927.150..18151.643 rows=1 loops=1)
   ->  Index Scan using index_jobs_on_due_date on jobs  (cost=0.42..1278647.41 rows=3466 width=2642) (actual time=16927.148..18151.641 rows=1 loops=1)
         Filter: (status = ANY ('{1,2,3,4}'::integer[]))
         Rows Removed by Filter: 595627
 Planning time: 0.205 ms
 Execution time: 18151.684 ms

Мы используем PostgreSQL 9.6.11 в AWS RDS.

В таблице ~ 500К строк.Поля для запроса:

  • due_date (отметка времени без часового пояса, может быть нулевой)
  • статус (целое, не нулевое)

Мыимеют следующие индексы:

CREATE INDEX index_jobs_on_due_date ON public.jobs USING btree (due_date)
CREATE INDEX index_jobs_on_due_date_and_status ON public.jobs USING btree (due_date, status)
CREATE INDEX index_jobs_on_status ON public.jobs USING btree (status)
CREATE UNIQUE INDEX jobs_pkey ON public.jobs USING btree (id)

Заранее спасибо, - Джек

1 Ответ

1 голос
/ 04 июня 2019

Для этого запроса:

SELECT  j.*
FROM "jobs" j
WHERE j."status" IN (1, 2, 3, 4)
ORDER BY "jobs"."due_date" ASC
LIMIT 5;

«Очевидный» индекс находится на (status).Но это может не помочь.Цель состоит в том, чтобы избавиться от сортировки.Таким образом, вы можете переписать запрос и использовать индекс jobs(status, due_date):

select j.*
from ((select j.*
       from jobs j
       where j.status = 1
       order by j.due_date asc
       limit 5
      ) union all
      (select j.*
       from jobs j
       where j.status = 2
       order by j.due_date asc
       limit 5
      ) union all
      (select j.*
       from jobs j
       where j.status = 3
       order by j.due_date asc
       limit 5
      ) union all
      (select j.*
       from jobs j
       where j.status = 4
       order by j.due_date asc
       limit 5
      )
     ) j
order by due_date
limit 5;

Каждый из подзапросов должен использовать составной индекс.Окончательная сортировка будет состоять из (максимум) 20 строк, что должно быть быстрым.*

Здесь можно использовать индекс для вычисления ROW_NUMBER().Это может потребовать полного сканирования таблицы.Но окончательная сортировка будет ограничена 20 строками, поэтому окончательная сортировка исключается.

...