Медленное сканирование индекса в большой таблице

12

Используя PostgreSQL 9.2, у меня проблемы с медленными запросами к относительно большой таблице (более 200 миллионов строк). Я не делаю ничего сумасшедшего, просто добавляю исторические ценности. Ниже приведен запрос и вывод плана запроса.

Моя таблица раскладок:

                                   Table "public.energy_energyentry"
  Column   |           Type           |                            Modifiers
-----------+--------------------------+-----------------------------------------------------------------
 id        | integer                  | not null default nextval('energy_energyentry_id_seq'::regclass)
 prop_id   | integer                  | not null
 timestamp | timestamp with time zone | not null
 value     | double precision         | not null
Indexes:
    "energy_energyentry_pkey" PRIMARY KEY, btree (id)
    "energy_energyentry_prop_id" btree (prop_id)
    "energy_energyentry_prop_id_timestamp_idx" btree (prop_id, "timestamp")
Foreign-key constraints:
    "energy_energyentry_prop_id_fkey" FOREIGN KEY (prop_id) REFERENCES gateway_peripheralproperty(id) DEFERRABLE INITIALLY DEFERRED

Данные варьируются с 2012-01-01 по настоящее время, постоянно добавляются новые данные. Во внешнем ключе около 2,2 тыс. Различных значений prop_id, распределенных равномерно.

Я заметил, что оценки строк не за горами, но оценки затрат кажутся в 4 раза больше. Вероятно, это не проблема, но могу ли я что-нибудь с этим сделать?

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

EXPLAIN ANALYZE 
SELECT SUM("value") 
FROM "energy_energyentry" 
WHERE 
  "prop_id"=82411 
  AND "timestamp">'2014-06-11' 
  AND "timestamp"<'2014-11-11'
;
 Aggregate  (cost=214481.45..214481.46 rows=1 width=8) (actual time=51504.814..51504.814 rows=1 loops=1)
   ->  Index Scan using energy_energyentry_prop_id_timestamp_idx on  energy_energyentry (cost=0.00..214434.08 rows=18947 width=8) (actual time=136.030..51488.321 rows=13578 loops=1)
         Index Cond: ((prop_id = 82411) AND ("timestamp" > '2014-06-11 00:00:00+00'::timestamp with time zone) AND ("timestamp" < '2014-11-11 00:00:00+00'::timestamp with time zone))
 Total runtime: 51504.841 ms

Любые предложения, как сделать это быстрее?
Я тоже в порядке, просто услышав, что я не сделал ничего странного.

Exelian
источник
1
Пожалуйста, расскажите нам, как выглядит ваша таблица, какие у нее индексы и разброс данных.
Colin 't Hart
Я добавил дополнительную информацию, которую вы спросили. Не знаю, пропустил ли я что-нибудь.
Экзелиан,
2
Странно: ваш анализ объяснения показывает prop_time_idx, а определение таблицы показывает entry_prop_id_timestamp_idx. Это тот же индекс? Пожалуйста исправьте.
Colin 't Hart
Если вы ссылаетесь на то, что «смета расходов, по-видимому, в 4 раза больше» на тот факт, что цифры стоимости примерно в 4 раза превышают фактическое время , то, пожалуйста, обратите внимание, что эти два показателя не имеют никакого отношения друг к другу. Стоимость - это только оценка, помогающая оптимизатору запросов выбрать наиболее привлекательный план. Вне этого контекста это обычно бессмысленная ценность.
Дезсо
1
Сколько процентов таблицы представляет ваш диапазон дат (без учета значений для prop)? Если бы только небольшой процент, возможно, индекс ("timestamp", prop)был бы лучше. Несколько индексов с одинаковыми ведущими столбцами ( propв вашем случае) также часто являются избыточными.
Colin 't Hart

Ответы:

10

Ваша таблица большая , как и любой индекс, охватывающий всю таблицу. При условии, что:

  • timestamp = now()вводятся только новые данные (с )
  • существующие строки не изменены и не удалены.
  • у вас есть данные с 2012-01-01, но запросы в основном на текущий год (?)

Я бы предложил частичный, многостолбцовый (охватывающий!) Индекс :

CREATE INDEX ON energy_energyentry (prop_id, "timestamp", value)
WHERE "timestamp" >= '2014-01-01 0:0';  -- adapt to your needs

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

Значение последнего столбца включено только для получения результатов сканирования только по индексу . Агрессивная настройка автоочистки может помочь, поддерживая карту видимости в актуальном состоянии, как уже упоминалось @jjanes .

Частичный индекс должен легче помещаться в ОЗУ и оставаться там дольше.

Возможно, вам придется включить это WHEREусловие в запросы, чтобы планировщик понял, что индекс применим к запросу, например:

SELECT sum(value) AS sum_value
FROM   energy_energyentry
WHERE  prop_id = 82411 
AND   "timestamp" > '2014-06-11 0:0' 
AND   "timestamp" < '2014-11-11 0:0'
AND   "timestamp" >= '2014-01-01 0:0'; -- seems redundant, but may be needed

Поскольку ваш запрос суммирует много строк ( rows=13578), это займет некоторое время, даже при сканировании только по индексу. Это не должно быть где-то около 50 секунд, хотя. Меньше секунды на любом наполовину приличном оборудовании.

Связанные (но игнорируйте CLUSTERи FILLFACTOR, и то , и другое не имеет значения, если вы можете получить из этого сканирование только по индексу) :

Помимо:
Так как вы в настоящее время имеют индекс (prop_id, "timestamp"), дополнительный индекс только (prop_id)может стоить больше , чем это стоит:

Эрвин Брандштеттер
источник
Теперь, когда Postgres поддерживает индексы BRIN, это будет полезно здесь? Я планирую хранить около 140 миллионов строк данных на postgres. Является ли BRIN правильным индексом для такой большой таблицы?
Арья
2

Если вы создадите индекс для (prop_id, "timestamp", "value"), то он может использовать сканирование только по индексу для вычисления значения, даже не посещая таблицу. Это может сэкономить много случайного доступа к диску.

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

jjanes
источник
Добавление значения может быть действительно интересным, я посмотрю, не ускорит ли это ситуацию. Есть ли у вас какие-либо предложения по настройке вакуума или документации, на которые я могу посмотреть?
Экзелиан,