2014-01-27 2 views
19

Мои запросы очень медленно, когда я добавляю limit 1.Запрос PostgreSQL очень медленный с ограничением 1

У меня есть таблица object_values с отметками времени значений для объектов:

timestamp | objectID | value 
-------------------------------- 
2014-01-27|  234 | ksghdf 

на объект Я хочу, чтобы получить последнее значение:

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC LIMIT 1; 

(я отменил запрос после более чем 10 минут)

Этот запрос очень медленный, если для данного объектаID нет значений (это быстро, если есть результаты). Если удалить предел он говорит мне почти мгновенно, что нет никаких результатов:

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC; 
... 
Time: 0.463 ms 

ОБЪЯСНИТЬ показывает мне, что запрос без ограничений использует индекс, где, как запрос с limit 1 не делает использование индекса :

Медленный запрос:

explain SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC limit 1; 
QUERY PLAN` 
---------------------------------------------------------------------------------------------------------------------------- 
Limit (cost=0.00..2350.44 rows=1 width=126) 
-> Index Scan Backward using object_values_timestamp on object_values (cost=0.00..3995743.59 rows=1700 width=126) 
    Filter: (objectID = 53708)` 

Быстрый запрос:

explain SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC; 
                QUERY PLAN 
-------------------------------------------------------------------------------------------------------------- 
Sort (cost=6540.86..6545.11 rows=1700 width=126) 
    Sort Key: timestamp 
    -> Index Scan using object_values_objectID on working_hours_t (cost=0.00..6449.65 rows=1700 width=126) 
     Index Cond: (objectID = 53708) 

В таблице содержит 44 884 555 строк и 66 762 различных идентификатора объекта.
У меня есть отдельные индексы на обоих полях: timestamp и objectID.
Я сделал vacuum analyze на столе, и я переделал таблицу.

Кроме того медленный запрос становится быстро, когда я установил лимит на 3 или выше:

explain SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC limit 3; 
                QUERY PLAN 
-------------------------------------------------------------------------------------------------------------------- 
Limit (cost=6471.62..6471.63 rows=3 width=126) 
    -> Sort (cost=6471.62..6475.87 rows=1700 width=126) 
     Sort Key: timestamp 
     -> Index Scan using object_values_objectID on object_values (cost=0.00..6449.65 rows=1700 width=126) 
       Index Cond: (objectID = 53708) 

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

Это настоящая причина? Есть ли решение для этого?

ответ

23

Вы столкнулись с проблемой, которая, как мне кажется, связана с отсутствием статистики по корреляциям строк. Подумайте о том, как сообщить об этом pg-bugs для справки, если используется последняя версия Postgres.

Толкование я предлагаю для ваших планов:

  • limit 1 делает Postgres искать одну строку, и при этом предполагается, что ваш object_id достаточно часто, что она будет отображаться разумно быстро в сканировании индекса.

    Основываясь на статистике, которую вы дали, ее мышление, вероятно, состоит в том, что в среднем нужно найти ~ 70 строк, чтобы найти одну строку, которая подходит; он просто не понимает, что object_id и timestamp коррелируют с точкой, в которой он действительно будет читать большую часть таблицы.

  • limit 3, напротив, делает его понимать, что это достаточно редко, поэтому он серьезно рассматривает (и в конце концов ...) топ-н сортировка ожидаемые 1700 строк с object_id вы хотите, на основании того, что делать это, вероятно, дешевле ,

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

  • no limit пункт означает, что он получит 1700 в любом случае, поэтому он идет прямо на индекс на object_id.

Решение, кстати: добавить индекс (object_id, timestamp) или (object_id, timestamp desc).

+0

Для случая с «пределом 1» вы имели в виду сканирование таблицы? Вы написали индексное сканирование – harmic

+0

@harmic: OP имеет индексное сканирование там ... не обязательно всей таблицы, но, конечно, гораздо больше, чем то, что думал PG. –

+0

Вы правы! Я только читаю текст OP, где он сказал, что не использует индекс. Но он выбирает сканировать индекс метки времени; странный выбор – harmic

18

Вы можете избежать этой проблемы, добавив ненужное предложение ORDER BY к запросу.

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp, objectID DESC limit 1; 
+1

HA! Это замечательно! Полностью исправляет это! – BrianC

+1

Этот ответ на самом деле работает, в отличие от ответа и всех комментариев выше. – mianos

+0

Это потрясающе! Просто поднимите мой запрос и можете использовать его во время выполнения. Благодаря! –

Смежные вопросы