Як змусити Postgres використовувати певний індекс?


112

Як змусити Postgres використовувати індекс, коли він інакше наполягатиме на виконанні послідовного сканування?



1
+1 Я хотів би побачити цю функцію. Справа не в тому, щоб просто відключити сканування послідовностей, як говорять інші відповіді: нам потрібна здатність змусити PG використовувати певний індекс . Це тому, що в реальному слові статистика може бути абсолютно помилковою, і в цей момент вам потрібно використовувати ненадійні / часткові обхідні шляхи. Я погоджуюся, що в простих випадках слід спочатку перевірити індекси та інші налаштування, але для надійності та розширеного використання великих даних нам це потрібно.
collimarco

У MySQL та Oracle це є… Не впевнений, чому планувальник Postgres такий ненадійний.
Кевін Паркер

Відповіді:


103

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

Як дуже тупий молоток, корисний для тестування, ви можете використовувати параметри enable_seqscanта enable_indexscan. Побачити:

Вони не підходять для постійного використання у виробництві . Якщо у вас є проблеми з вибором плану запитів, вам слід переглянути документацію для відстеження проблем із виконанням запитів . Не просто встановлюйте enable_парами і йдіть геть.

Якщо у вас немає дуже вагомих причин для використання індексу, Postgres може зробити правильний вибір. Чому?

  • Для невеликих таблиць швидше робити послідовне сканування.
  • Postgres не використовує індекси, коли типи даних не відповідають належним чином, можливо, вам потрібно буде включити відповідні касти.
  • Можливо, ваші налаштування планувальника спричинять проблеми.

Дивіться також цю стару публікацію групи новин .


4
Погоджено, змушуючи postgres робити це так, як правило, означає, що ви зробили це неправильно. 9/10 разів планувальник обіграє все, що ви можете придумати. Інший раз, тому що ви помилилися.
Кент Фредрік

Я думаю, що це гарна ідея перевірити справді класи операторів вашого індексу.
metdos

2
Я ненавиджу відроджувати старе питання, але я часто бачу в документації Postgres, дискусіях і тут, але чи є узагальнена концепція того, що відповідає малій таблиці ? Це щось на зразок 5000 рядків чи 50000 тощо?
waffl

1
@waffl Чи розглядали ви бенчмаркінг? Створіть просту таблицю з індексом та супутньою функцією для заповнення її з n рядів випадкових барахлів. Потім почніть переглядати план запитів для різних значень n . Коли ви побачите, що він починає використовувати індекс, у вас повинна бути відповідь на тему. Ви також можете отримати послідовне сканування, якщо PostgreSQL визначить (на основі статистики), що сканування індексу також не збирається ліквідувати дуже багато рядків. Тож бенчмаркінг - це завжди хороша ідея, коли у вас є реальні проблеми щодо ефективності. Я б сказав, що анекдотична здогадка, пару тисяч, як правило, "маленька".
jpmc26

11
Маючи 30-річний досвід роботи на таких платформах, як Oracle, Teradata та MSSQL, я вважаю, що оптимізатор PostgreSQL 10 не є особливо розумним. Навіть за допомогою актуальної статистики він генерує менш ефективні плани виконання, ніж вимушений у спеціальному напрямку. Надання структурних підказок для компенсації цих проблем допоможе вирішити можливість PostgreSQL рости в більшій кількості сегментів ринку. ІМХО.
Гвідо Ліндерс

75

Напевно, єдина поважна причина використання

set enable_seqscan=false

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


41
ця коротка відповідь насправді дає добру підказку для тестування
dwery

3
На питання ніхто не відповідає!
Іваїло Бардаров

@IvailoBardarov Причина всіх цих інших пропозицій тут полягає в тому, що PostgreSQL не має цієї функції; це було усвідомлене рішення, яке приймали розробники, виходячи з того, як це зазвичай використовується та довгострокових проблем, які він викликає.
jpmc26

Хороший трюк для перевірки: запустіть set enable_seqscan=false, запустіть свій запит, а потім швидко запустіть, set enable_seqscan=trueщоб повернути postgresql до його належної поведінки (і, очевидно, цього не робити у виробництві, лише в розробці!)
Брайан Хеллекін

2
@BrianHellekin Краще, SET SESSION enable_seqscan=falseщоб вплинути лише на себе
Ізката

20

Іноді 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

3
Хороша ідея. Однак, коли ми вимикаємо використання поточного індексу за допомогою цього методу - підсистема оптимізатора запитів постгресqл переходить до наступного відповідного індексу. Таким чином, жодної гарантії, що оптимізатор не вибере your_wanted_index, це може бути так, що двигун postgresql просто виконуватиме сканування послідовності / первинного ключа. Висновок - не існує 100% надійного методу, який би застосував деяке використання індексу для сервера PostgreSql.
Агній Василіяускас

Що робити, якщо немає whereумови, але дві таблиці або об'єднані, і Postgres не вдається взяти індекс.
Luna

@Surya вищезазначене стосується як ДЕРЖАВИ, так і для
приєднання

18

Коротка відповідь

Ця проблема, як правило, виникає тоді, коли передбачувана вартість сканування індексу занадто висока і неправильно відображає реальність. Можливо, вам доведеться опустити 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 для явного кешування даних.



2
Мені навіть довелося встановити random_page_cost = 0,1, щоб змусити сканування індексу працювати на великій (~ 600М рядків рядків) в Pg 10.1 на Ubuntu. Без налаштування, сканування послідовності (незважаючи на паралельність) займало 12 хв. (Зверніть увагу, що таблицю аналізу виконували!). Привід SSD. Після налаштування час виконання виконується за 1 секунду.
Анатолій Алексєєв

Ти врятував мені день. Я сходив з розуму, намагаючись зрозуміти, як саме той самий запит на одній базі даних займає 30 секунд на одній машині і менше 1 на іншій, навіть після запуску аналізу на обох кінцях ... Кому це може стосуватися: команда ' ALTER SYSTEM SET random_page_cost = x 'встановлює нове значення за замовчуванням у всьому світі.
Жульєн

10

Питання про себе дуже недійсне. Примусові дії (наприклад, enable_seqscan = вимкнено) - дуже погана ідея. Можливо, буде корисно перевірити, чи буде вона швидше, але у виробничому коді ніколи не слід використовувати такі хитрощі.

Натомість - поясніть аналіз свого запиту, прочитайте його та з’ясуйте, чому PostgreSQL вибирає поганий (на ваш погляд) план.

В Інтернеті є інструменти, які допомагають з читанням пояснити аналіз результатів - один з них - обяснено.depesz.com - написаний мною.

Ще один варіант - приєднатись до каналу #postgresql у мережі freenode irc та поговорити з хлопцями там, щоб допомогти вам - оскільки оптимізація запиту - це не питання "задайте питання, отримайте відповідь, будьте щасливі". це більше схоже на розмову, з багатьма речами, які потрібно перевірити, багато чого слід дізнатися.


2

Існує хитрість, щоб просувати постгреси, щоб віддати перевагу seqscan, додаючи OFFSET 0в підзапит

Це зручно для оптимізації запитів, що пов'язують великі / величезні таблиці, коли все, що вам потрібно, - це лише n перших / останніх елементів.

Скажімо, ви шукаєте перші / останні 20 елементів, що містять декілька таблиць, що містять 100 к (або більше) записів, не має сенсу будувати / з’єднувати весь запит за всіма даними, коли те, що ви шукаєте, буде в перших 100 або 1000 записи. Наприклад, у цьому сценарії виявляється, що в 10 разів швидше зробити послідовне сканування.

див. Як я можу запобігти Postgres вкладати підзапит?


Гарний трюк. Хоча, звичайно, хороший оптимізатор повинен оптимізувати компенсацію 0 :-)
Guido Leenders
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.