Повільне сканування індексу у великій таблиці


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 до теперішнього часу, постійно додаються нові дані. У prop_idзовнішньому ключі є близько 2,2 тис. Чітких значень , розподілених рівномірно.

Я зауважую, що рядкові оцінки не за горами, але оцінки витрат здаються більшими на 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

Будь-які пропозиції, як зробити це швидше?
Я також добре, лише почувши, що я нічого дивного не робив.


1
Скажіть, будь ласка, як виглядає ваша таблиця, які індекси має та поширення даних.
Colin 't Hart

Я додав додаткову інформацію, яку ви запитували. Не знаю, я щось пропустив.
Екселіан

2
Дивно: ваш пояснення аналізує шоу prop_time_idx, але визначення таблиці показує entry_prop_id_timestamp_idx. Це той самий показник? Виправте, будь ласка.
Colin 't Hart

Якщо ви посилаєтесь на "кошторис витрат, здається, в 4 рази більший" на той факт, що кількість витрат приблизно в 4 рази перевищує фактичний час , то зауважте, що ці дані не мають нічого спільного. Вартість - це лише оцінка, що допомагає оптимізатору запитів вибрати найкращий план. Поза цим контекстом це зазвичай безглузда цінність.
dezso

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, "timetamp", "value"), то він може використовувати сканування лише для індексу для обчислення значення, не відвідуючи таблицю. Це може врятувати багато випадкового доступу до диска.

Щоб отримати максимальну користь, потрібно бути агресивними щодо пилососити стіл. Налаштування автовиключення за замовчуванням недостатньо агресивні для таблиць, які є лише вставками, в яких ви хочете ефективно підтримувати сканування лише для індексів.


Додавання значення може бути справді цікавим, я погляну, чи це прискорить справи. Чи є якісь пропозиції щодо налаштувань вакууму чи документації, яку я можу переглянути?
Екселіан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.