Почему следующий запрос Postgres SQL занимает так много времени? - PullRequest
5 голосов
/ 30 мая 2011

Необработанный запрос выглядит следующим образом

SELECT "TIME", "TRADEPRICE"
  FROM "YEAR" where "DATE"='2010-03-01' 
  and "SECURITY"='STW.AX' 
  AND "TIME" < '10:16:00' 
  AND "TYPE" = 'TRADE'
  ORDER BY "TIME" ASC LIMIT 3

Я построил три индекса следующим образом

Columns "DATE" DESC NULLS LAST
Columns "SECURITY" DESC NULLS LAST
Columns "TIME" DESC NULLS LAST

Я не индексирую TYPE, поскольку он принимает только одно из двух возможных значений

Объяснить, анализ производит следующее

"Limit  (cost=50291.28..50291.28 rows=3 width=16) (actual time=1794484.566..1794484.567 rows=3 loops=1)"
"  ->  Sort  (cost=50291.28..50291.29 rows=4 width=16) (actual time=1794484.562..1794484.563 rows=3 loops=1)"
"        Sort Key: "TIME""
"        Sort Method:  top-N heapsort  Memory: 25kB"
"        ->  Bitmap Heap Scan on "YEAR"  (cost=48569.54..50291.24 rows=4 width=16) (actual time=1794411.662..1794484.498 rows=20 loops=1)"
"              Recheck Cond: (("SECURITY" = 'STW.AX'::bpchar) AND ("DATE" = '2010-03-01'::date))"
"              Filter: (("TIME" < '10:16:00'::time without time zone) AND ("TYPE" = 'TRADE'::bpchar))"
"              ->  BitmapAnd  (cost=48569.54..48569.54 rows=430 width=0) (actual time=1794411.249..1794411.249 rows=0 loops=1)"
"                    ->  Bitmap Index Scan on security_desc  (cost=0.00..4722.94 rows=166029 width=0) (actual time=1793917.506..1793917.506 rows=1291933 loops=1)"
"                          Index Cond: ("SECURITY" = 'STW.AX'::bpchar)"
"                    ->  Bitmap Index Scan on date_desc  (cost=0.00..43846.35 rows=2368764 width=0) (actual time=378.698..378.698 rows=2317130 loops=1)"
"                          Index Cond: ("DATE" = '2010-03-01'::date)"
"Total runtime: 1794485.224 ms"

База данных - это около 1 миллиарда строк, работающих на Core2Quad с 8 ГБ ОЗУ на Ubuntu 64bit. Конечно, этот запрос не должен занимать полчаса

Ответы [ 3 ]

12 голосов
/ 30 мая 2011

База данных - это около 1 миллиарда строк, работающих на Core2Quad с 8 ГБ ОЗУ на Ubuntu 64bit. Конечно, этот запрос не должен занимать полчаса

Это занимает полчаса из-за того, как вы настраиваете свои индексы.

Ваш запрос не имеет многостолбцовых индексов, которые он может использовать для перехода прямо к нужным строкам. Это делает следующее лучшее: сканирование растровых индексов по едва отобранным индексам и сортировка топ-3 результирующего набора.

Два рассматриваемых индекса, по ценным бумагам и на дату, дают 1,3 млн и 2,3 млн строк соответственно. Их объединение будет мучительно медленным, потому что вы случайно просматриваете более миллиона строк и фильтруете каждую из них.

Добавляя оскорбление, ваша структура данных такова, что два сильно коррелированных поля (дата и время) хранятся и обрабатываются отдельно. Это сбивает с толку планировщика запросов, поскольку Postgres не собирает данные корреляции. Таким образом, ваши запросы почти всегда прибегают к фильтрации огромных наборов данных и упорядочению отфильтрованного набора по отдельным критериям.

Я бы предложил следующие изменения:

  1. Измените таблицу и добавьте столбец datetime типа timestamp with time zone. Объедините в нем свои столбцы даты и времени.

  2. Удалите соответственно поля даты и времени, а также индексы на них. Также сбросьте индекс безопасности.

  3. Создать индекс (безопасность, дата и время). (И не возитесь с нулями в первую очередь / нулями в последнюю очередь, если ваши критерии заказа не содержат эти пункты тоже.)

  4. По вашему усмотрению добавьте отдельный индекс on (datetime) или on (datetime, security), если вам когда-либо понадобится выполнить запросы, которые выполняют статистику по всем сделкам за день или диапазон дат.

  5. проанализируйте весь беспорядок, как только вы закончите с вышеописанным.

После этого вы сможете переписать ваш запрос следующим образом:

SELECT "TIME", "TRADEPRICE"
  FROM "YEAR"
  WHERE '2010-03-01 00:00:00' <= "DATETIME" AND "DATETIME" < '2010-03-01 10:16:00'
  AND "SECURITY"='STW.AX' 
  AND "TYPE" = 'TRADE'
  ORDER BY "DATETIME" ASC LIMIT 3

Это даст наиболее оптимальный план: получение трех верхних строк из отфильтрованного сканирования индекса (безопасность, дата и время), которое, как я ожидаю (поскольку у вас миллиард строк), займет максимум 25 мс.

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

Добавьте сложный индекс многих поисковых терминов вместе, например, ON YEAR (TYPE, SECURITY, DATE, TIME). Затем база данных может искать в одном индексе, чтобы сопоставить их все, вместо того, чтобы искать по нескольким индексам и сопоставлять все результаты вместе (Bitmap Index Scan).

Какие именно столбцы (например, включают TYPE или нет?) И в каком порядке вы включаете их в индекс, зависит от характеристик данных и от того, какие другие запросы вы выполняете (поскольку вы можете повторно использовать любые левые подмножество составного индекса бесплатно), поэтому немного поэкспериментируйте; но для стимулирования оптимизации заказа оставьте столбец ORDER BY последним использованным индексным столбцом / направлением.

Возможно, вы также захотите ANALYZE обновить статистику для планировщика запросов, поскольку некоторые предположения о количестве строк кажутся немного неправильными.

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

Я не индексирую TYPE, потому что он принимает только одно из двух возможных значений

Вы должны понимать, как работают индексы и почему они работают.Индексы дублируют индексированные данные в небольшие блоки индекса, которые содержат только указанные данные индекса.Из ваших X GB необработанных данных остается только размер X / 20 (предположительный).Если вы укажете запрос, который использует данные, которые не проиндексированы, это означает, что для каждой из записей, которые удовлетворяют другим критериям запроса, СУБД должна считывать соответствующий блок необработанных данных в блок индекса, чтобы определить, соответствует ли он критериям запроса.

Оптимальный случай - это, по крайней мере, один индекс, который содержит каждое требование, указанное в запросе, поэтому нет необходимости выполнять поиск в блоках данных.

Еще один совет: обычно этохорошая идея перечислить столбцы, которые принимают значения, которые обычно запрашиваются в виде диапазона (в вашем случае «ВРЕМЯ») в последний раз.

Мое предложение: отбросить все индексы.Создайте индекс с полями TIME (ASC), DATE, SECURITY, TYPE (в этом порядке).Используйте запрос

SELECT "TIME", "TRADEPRICE"
  FROM "YEAR"
  WHERE "TIME" < '10:16:00'
  AND "DATE"='2010-03-01' 
  AND "SECURITY"='STW.AX' 
  AND "TYPE" = 'TRADE'

И наблюдайте за невероятной скоростью.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...