Низкая производительность БД при использовании ORDER BY - PullRequest
4 голосов
/ 03 мая 2011

Я работаю с некоммерческой организацией, которая занимается планированием солнечного потенциала в США.Само собой разумеется, у нас есть смехотворно большая база данных PostgreSQL 9.Выполнение запроса, подобного показанному ниже, выполняется быстро до тех пор, пока строка order by не будет закомментирована, и в этом случае выполнение одного и того же запроса занимает вечность (185 мс без сортировки по сравнению с 25 минутами с).Какие шаги необходимо предпринять, чтобы этот и другие запросы выполнялись в более управляемое и разумное время?

select  A.s_oid, A.s_id, A.area_acre, A.power_peak, A.nearby_city, A.solar_total 
from global_site A cross join na_utility_line B
where (A.power_peak between 1.0 AND  100.0)
and A.area_acre >= 500
and A.solar_avg >= 5.0
AND A.pc_num <= 1000
and (A.fips_level1 = '06'  AND A.fips_country = 'US' AND A.fips_level2 = '025')
and B.volt_mn_kv >= 69
and B.fips_code like '%US06%'
and B.status = 'active'
and ST_within(ST_Centroid(A.wkb_geometry), ST_Buffer((B.wkb_geometry), 1000))
--order by A.area_acre
offset 0 limit 11;

Ответы [ 7 ]

5 голосов
/ 04 мая 2011

Сортировка не является проблемой - на самом деле стоимость процессора и памяти для сортировки близка к нулю, поскольку Postgres имеет сортировку Top-N, где результирующий набор сканируется, сохраняя в актуальном состоянии небольшой буфер сортировки, содержащий только верхнюю часть -N строк.

select count(*) from (1 million row table)               -- 0.17 s
select * from (1 million row table) order by x limit 10; -- 0.18 s
select * from (1 million row table) order by x;          -- 1.80 s

Таким образом, вы видите, что сортировка Top-10 добавляет только 10 мс к быстрому быстрому подсчету (*) по сравнению с намного более длинной для реальной сортировки. Это очень удобная функция, я часто ее использую.

Хорошо, теперь без EXPLAIN ANALYZE невозможно быть уверенным, но я чувствую, что настоящая проблема заключается в перекрестном соединении. По сути, вы фильтруете строки в обеих таблицах, используя:

where (A.power_peak between 1.0 AND  100.0)
and A.area_acre >= 500
and A.solar_avg >= 5.0
AND A.pc_num <= 1000
and (A.fips_level1 = '06'  AND A.fips_country = 'US' AND A.fips_level2 = '025')

and B.volt_mn_kv >= 69
and B.fips_code like '%US06%'
and B.status = 'active'

OK. Я не знаю, сколько строк выбрано в обеих таблицах (скажет только EXPLAIN ANALYZE), но это, вероятно, важно. Знание этих цифр поможет.

Тогда мы получили наихудшее условие CROSS JOIN когда-либо:

and ST_within(ST_Centroid(A.wkb_geometry), ST_Buffer((B.wkb_geometry), 1000))

Это означает, что все строки A сопоставляются со всеми строками B (поэтому это выражение будет оцениваться большое количество раз), используя набор довольно сложных, медленных и ресурсоемких функций.

Конечно, это ужасно медленно!

Когда вы удаляете ORDER BY, postgres просто появляется (случайно?) С кучей совпадающих строк в самом начале, выводит их и останавливается после достижения LIMIT.

Вот небольшой пример:

Таблицы a и b идентичны и содержат 1000 строк и столбец типа BOX.

select * from a cross join b where (a.b && b.b)     --- 0.28 s

Здесь 1000000 тестов перекрытия (оператор &&) завершаются за 0,28 с. Набор тестовых данных создается таким образом, чтобы набор результатов содержал только 1000 строк.

create index a_b on a using gist(b);
create index b_b on a using gist(b);
select * from a cross join b where (a.b && b.b)     --- 0.01 s

Здесь индекс используется для оптимизации перекрестного соединения, а скорость смешная.

Вам необходимо оптимизировать это соответствие геометрии.

  • добавить столбцы, которые будут кешироваться:
    • ST_Centroid (A.wkb_geometry)
    • ST_Buffer ((B.wkb_geometry), 1000)

НЕТ ПУНКТА в повторном вычислении этих медленных функций миллион раз во время CROSS JOIN, поэтому сохраняйте результаты в столбце. Используйте триггер, чтобы держать их в курсе.

  • добавить столбцы типа BOX, которые будут кешироваться:

    • Ограничительная рамка ST_Centroid (A.wkb_geometry)
    • Ограничительная коробка ST_Buffer ((B.wkb_geometry), 1000)
  • добавить индексы в боксы

  • добавить тест перекрытия Box (используя оператор &&), который будет использовать индекс

  • оставьте свой ST_Within, который будет действовать как конечный фильтр для строк, которые проходят

Может быть, вы можете просто проиндексировать столбцы ST_Centroid и ST_Buffer ... и использовать (проиндексированный) оператор "содержит", см. Здесь:

http://www.postgresql.org/docs/8.2/static/functions-geometry.html

2 голосов
/ 03 мая 2011

Я бы предложил создать индекс для area_acre.Возможно, вы захотите взглянуть на следующее: http://www.postgresql.org/docs/9.0/static/sql-createindex.html

Я бы порекомендовал делать такие вещи в часы пик, потому что это может быть довольно интенсивно при большом количестве данных.Что касается индексов, вам также нужно обратить внимание на перестройку их по расписанию, чтобы обеспечить производительность с течением времени.Опять же, этот график должен выходить за пределы часов пик.

Возможно, вы захотите взглянуть на эту статью от коллеги SO'er и его опыта по замедлению работы базы данных с течением времени с помощью индексов: Почему PostgresQL запрашиваетпроизводительность падает со временем, но восстанавливается при восстановлении индекса

1 голос
/ 03 мая 2011

Если поле A.area_acre не проиндексировано, это может замедлить его.Вы можете запустить запрос с помощью EXPLAIN, чтобы увидеть, что он делает во время выполнения.

0 голосов
/ 27 марта 2014

Рассматривали ли вы создание индексов на основе выражений в интересах соединений хайриеров и условий?

0 голосов
/ 04 мая 2011

Я не знаком с оптимизацией PostgreSQL, но похоже, что то, что происходит, когда запрос выполняется с предложением ORDER BY, состоит в том, что создается весь набор результатов, затем он сортируется, а затем верхние 11 строквзят из этого отсортированного результата.Без ORDER BY механизм запросов может просто сгенерировать первые 11 строк в любом порядке и затем это будет сделано.

Наличие индекса в поле area_acre весьма вероятно, может не помочь для сортировки (ORDER BY) в зависимости от того, как построен набор результатов.Теоретически его можно использовать для генерации результирующего набора путем обхода таблицы global_site с использованием индекса на area_acre;в этом случае результаты будут сгенерированы в желаемом порядке (и он может остановиться после генерации 11 строк в результате).Если он не генерирует результаты в указанном порядке (и кажется, что это не так), тогда этот индекс не поможет в сортировке результатов.

Одна вещь, которую вы можете попробовать, это удалить «CROSS JOIN» из запроса.Я сомневаюсь, что это будет иметь значение, но оно того стоит.Поскольку предложение WHERE используется для объединения двух таблиц (через ST_WITHIN), я считаю, что результат совпадает с внутренним соединением.Возможно, что использование синтаксиса CROSS JOIN приводит к тому, что оптимизатор делает нежелательный выбор.

В противном случае (кроме проверки наличия индексов для полей, которые фильтруются), вы можете немного поигратьУгадайка с запросом.Выделяется одно условие - area_acre >= 500.Это означает, что механизм запросов рассматривает все строки, которые удовлетворяют этому условию.Но тогда берутся только первые 11 строк.Вы можете попробовать изменить его на area_acre >= 500 and area_acre <= somevalue.somevalue - это угадывающая часть, которая нуждается в корректировке, чтобы убедиться, что вы получите не менее 11 строк.Это, однако, выглядит довольно глупо, поэтому я упоминаю об этом с некоторой нерешительностью.

0 голосов
/ 03 мая 2011

Первое, на что нужно обратить внимание - есть ли у вас индекс по полю, по которому вы упорядочиваете.Если нет, добавление одного значительно улучшит производительность.Я не очень хорошо знаю postgresql, но что-то похожее на:

CREATE INDEX area_acre ON global_site(area_acre)

Как отмечалось в других ответах, процесс индексирования интенсивен при работе с большим набором данных, так что делайте это в непиковый период.

0 голосов
/ 03 мая 2011

Прежде всего, я хотел бы взглянуть на создание индексов, убедиться, что ваша БД очищена, увеличить общие буферы для вашей установки БД, настройки work_mem.

...