Як змусити Postgres використовувати індекс, коли він інакше наполягатиме на виконанні послідовного сканування?
Як змусити Postgres використовувати індекс, коли він інакше наполягатиме на виконанні послідовного сканування?
Відповіді:
Якщо припустити, що ви запитуєте про загальну функцію "натякання на індекс", яку можна знайти у багатьох базах даних, PostgreSQL не надає такої функції. Це було свідоме рішення, прийняте командою PostgreSQL. Хороший огляд того, чому і що ви можете зробити замість цього, можна знайти тут . Причини в основному полягають у тому, що це хакер продуктивності, який, як правило, спричиняє більше проблем в подальшому вниз по мірі зміни даних, тоді як оптимізатор PostgreSQL може переоцінити план на основі статистичних даних. Іншими словами, що може бути хорошим планом запитів сьогодні, ймовірно, не буде хорошим планом запитів на весь час, а підказки-індекси змушують певний план запитів на весь час.
Як дуже тупий молоток, корисний для тестування, ви можете використовувати параметри enable_seqscan
та enable_indexscan
. Побачити:
Вони не підходять для постійного використання у виробництві . Якщо у вас є проблеми з вибором плану запитів, вам слід переглянути документацію для відстеження проблем із виконанням запитів . Не просто встановлюйте enable_
парами і йдіть геть.
Якщо у вас немає дуже вагомих причин для використання індексу, Postgres може зробити правильний вибір. Чому?
Дивіться також цю стару публікацію групи новин .
Напевно, єдина поважна причина використання
set enable_seqscan=false
це коли ви пишете запити і хочете швидко зрозуміти, яким би був насправді план запитів, якби велика кількість даних знаходилась у таблицях. Або, звичайно, якщо вам потрібно швидко підтвердити, що ваш запит не використовує індекс просто тому, що набір даних занадто малий.
set enable_seqscan=false
, запустіть свій запит, а потім швидко запустіть, set enable_seqscan=true
щоб повернути postgresql до його належної поведінки (і, очевидно, цього не робити у виробництві, лише в розробці!)
SET SESSION enable_seqscan=false
щоб вплинути лише на себе
Іноді PostgreSQL не вдається зробити найкращий вибір індексів для певної умови. Як приклад, припустимо, існує таблиця транзакцій з кількома мільйонами рядків, з яких є кілька сотень за будь-який день, а таблиця має чотири індекси :action_id, client_id, дата та опис. Ви хочете виконати такий запит:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description = 'Refund'
GROUP BY client_id
PostgreSQL може вирішити використовувати індекс_запис_запис_індекс замість транзакцій_да_ікс, що може призвести до того, що запит займе кілька хвилин замість менше однієї секунди. Якщо це так, ви можете змусити використовувати індекс на дату, змінивши такий стан:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description||'' = 'Refund'
GROUP BY client_id
your_wanted_index
, це може бути так, що двигун postgresql просто виконуватиме сканування послідовності / первинного ключа. Висновок - не існує 100% надійного методу, який би застосував деяке використання індексу для сервера PostgreSql.
where
умови, але дві таблиці або об'єднані, і Postgres не вдається взяти індекс.
Ця проблема, як правило, виникає тоді, коли передбачувана вартість сканування індексу занадто висока і неправильно відображає реальність. Можливо, вам доведеться опустити random_page_cost
конфігураційний параметр, щоб виправити це. З документації Postgres :
Зниження цього значення [...] призведе до того, що система надає перевагу скануванню індексів; підвищення його зробить сканування індексів порівняно дорожчим.
Ви можете перевірити, чи насправді нижнє значення змусить Postgres використовувати індекс (але використовувати це лише для тестування ):
EXPLAIN <query>; # Uses sequential scan
SET random_page_cost = 1;
EXPLAIN <query>; # May use index scan now
Ви можете відновити значення за замовчуванням SET random_page_cost = DEFAULT;
заново.
Індексне сканування вимагає не послідовних завантажень сторінок диска. Postgres використовує random_page_cost
для оцінки вартості таких непослідовних виборців по відношенню до послідовної вибірки. Значенням за замовчуванням є 4.0
, таким чином, припускаючи середній коефіцієнт вартості 4 порівняно з послідовними виборами (з урахуванням ефектів кешування).
Однак проблема полягає в тому, що це значення за замовчуванням не підходить для таких важливих сценаріїв реального життя:
1) твердотільні накопичувачі
Як свідчить документація:
Зберігання, що має низьку випадкову вартість зчитування відносно послідовних, наприклад твердотільних накопичувачів, може бути краще моделювати з меншим значенням для
random_page_cost
.
Відповідно до останнього пункту цього слайду з виступу на PostgresConf 2018, random_page_cost
слід встановити щось середнє 1.0
та 2.0
твердотільне накопичувачі.
2) Кешовані дані
Якщо необхідні дані індексу вже кешовані в ОЗУ, сканування індексу завжди буде значно швидше, ніж послідовне сканування. Документація говорить:
Відповідно, якщо ваші дані, ймовірно, повністю знаходяться в кеші, зменшення [...]
random_page_cost
може бути доцільним.
Проблема полягає в тому, що ви, звичайно, не можете легко знати, чи є вже кешовані відповідні дані. Однак якщо конкретний індекс часто запитується, і якщо система має достатню оперативну пам’ять, то дані, ймовірно, будуть кешовані та random_page_cost
повинні бути встановлені на менше значення. Вам доведеться поекспериментувати з різними значеннями і подивитися, що для вас працює.
Ви також можете використовувати розширення pg_prewarm для явного кешування даних.
Питання про себе дуже недійсне. Примусові дії (наприклад, enable_seqscan = вимкнено) - дуже погана ідея. Можливо, буде корисно перевірити, чи буде вона швидше, але у виробничому коді ніколи не слід використовувати такі хитрощі.
Натомість - поясніть аналіз свого запиту, прочитайте його та з’ясуйте, чому PostgreSQL вибирає поганий (на ваш погляд) план.
В Інтернеті є інструменти, які допомагають з читанням пояснити аналіз результатів - один з них - обяснено.depesz.com - написаний мною.
Ще один варіант - приєднатись до каналу #postgresql у мережі freenode irc та поговорити з хлопцями там, щоб допомогти вам - оскільки оптимізація запиту - це не питання "задайте питання, отримайте відповідь, будьте щасливі". це більше схоже на розмову, з багатьма речами, які потрібно перевірити, багато чого слід дізнатися.
Існує хитрість, щоб просувати постгреси, щоб віддати перевагу seqscan, додаючи OFFSET 0
в підзапит
Це зручно для оптимізації запитів, що пов'язують великі / величезні таблиці, коли все, що вам потрібно, - це лише n перших / останніх елементів.
Скажімо, ви шукаєте перші / останні 20 елементів, що містять декілька таблиць, що містять 100 к (або більше) записів, не має сенсу будувати / з’єднувати весь запит за всіма даними, коли те, що ви шукаєте, буде в перших 100 або 1000 записи. Наприклад, у цьому сценарії виявляється, що в 10 разів швидше зробити послідовне сканування.