Зведення до мінімуму індексованих читань зі складними критеріями


12

Я оптимізую базу даних робочих квитків Firebird 2.5. Вони зберігаються в таблиці, оголошеній як такий:

CREATE TABLE TICKETS (
  TICKET_ID id PRIMARY KEY,
  JOB_ID id,
  ACTION_ID id,
  STATUS str256 DEFAULT 'Pending'
);

Я, як правило, хочу знайти перший квиток, який не був оброблений і знаходиться у Pendingстатусі.

Мій цикл обробки буде:

  1. Отримайте перший квиток, куди Pending
  2. Робіть роботу з квитком.
  3. Оновити статус квитка => Complete
  4. Повторіть.

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

У мене індекс Status, але все ще здається, він сканує Ticket_Idкожну ітерацію стовпця. Здається, я щось пропускаю, але я не впевнений у чому. Чи очікується збільшилася кількість індексованих читання для чогось подібного, чи індекс погано поводиться?

- Редагування коментарів -

У Firebird ви обмежуєте пошук рядків таким чином:

Select First 1
  Job_ID, Ticket_Id
From
  Tickets
Where
  Status = 'Pending'

Тож коли я кажу "першим", я просто запитую, де обмежений набір записів, де Status = 'Pending'.


Що ви маєте на увазі під "першим" у "Отримати перший квиток, де" Очікує " ?
ypercubeᵀᴹ

Якщо "перший" означає найменший ticket_id, вам потрібен індекс на(status, ticket_id)
ypercubeᵀᴹ

І наскільки ви впевнені, що зниження продуктивності спричинене цією процедурою, а не іншими запитами / заявами?
ypercubeᵀᴹ

@ypercube - Ні, я не впевнений, що там деградація продуктивності. Ось чому моє запитання було: «чи потрібно мені цим займатися, чи це нормальна поведінка індексу?». Це щось я помітив під час моніторингу бази даних, і я вважав це несподіваним. Я б не очікував, що він продовжить сканувати попередні рядки, коли я надаю пункт де індексований стовпець. FWIW, модифікація індексу на включення ticket_idнасправді виконується гірше, ніж просто індексування статусу.
gddc

Чи id(тип даних) визначений вами домен?
a_horse_with_no_name

Відповіді:


1

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

У SQL Server (а може бути, і інших RDBMS?) Це можна вирішити за допомогою відфільтрованих індексів. У SQL Server ви додасте умову WHERE в кінці визначення свого індексу, щоб сказати "застосувати цей індекс лише до записів зі статусом <> 'Повноцінно" ". Тоді будь-який запит, що використовує цей предикат, швидше за все, буде використовувати індекс на невелику кількість записів, не встановлену на "Завершено". Однак, виходячи з документації тут: http://www.firebirdsql.org/refdocs/langrefupd25-ddl-index.html , схоже, Firebird не підтримує відфільтровані індекси.

Вирішення завдання полягає в тому, щоб помістити "Завершити" записи в таблицю ArchiveTickets. Створіть таблицю з точно таким же визначенням (хоча і без автоматично створеного ідентифікатора), як і ваша таблиця квитків, і підтримуйте рядки між ними, натискаючи «Завершити» записи до таблиці ArchiveTickets. Індекс у вашій таблиці квитків тоді буде мати значно меншу кількість записів та матиме високу ефективність. Це, ймовірно, означає, що вам потрібно буде змінити будь-які звіти тощо, які посилаються на "Завершити" квитки, щоб вказувати на таблицю Архів або виконувати UNION через квитки та ArchiveTickets. Це матиме перевагу не лише у швидкому, але й означатиме, що ви можете створити конкретні індекси для таблиці ArchiveTickets, щоб покращити її виконання для інших запитів (наприклад:

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


0

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

Ваш перший очікуваний запит не є детермінованим. Спочатку згідно з яким порядком? Таблиця SQL не має внутрішнього порядку; First 1хак просто дає вам деяку довільну першу. Щоб зробити це детермінованим, чому б не обробити очікувані завдання в порядку Job_ID?

Якщо у вас є два індекси {Job_ID} і {Status, Job_ID}, цей запит поверне один рядок передбачувано та ефективно:

Select Job_ID, Ticket_Id
From   Tickets
Where Job_ID = ( 
  select min(Job_ID) from Tickets 
  where Status = 'Pending'
);

Я не користувач Firebird, тому вам доведеться перевірити план запитів, але він повинен бути ефективним, оскільки підзапит посилається лише на другий індекс, дає значення для першого. (Можливо, вам доступні інші прийоми ефективності. Ви можете організувати фізичну таблицю у вигляді дерева B + або мати доступ, наприклад, до прихованої row_id.)

Інша зміна, яку я вніс би для коректності, - це зробити Statusєдиний обмежений байт і дозволити додатку подавати рядок "Очікує". Це захистить від помилкових Statusзначень і, ймовірно, зробить індекс меншим в угоді. Щось на зразок:

CREATE TABLE TICKETS (
  TICKET_ID id PRIMARY KEY,
  JOB_ID id,
  ACTION_ID id,
  STATUS char(1) not NULL 
     DEFAULT 'P'
     CHECK( STATUS in ('P', 'C', 'X') ) -- whatever the domain is
);

Звичайно, ви можете використовувати представлення (або, можливо, похідний стовпець), щоб подати канонічні рядки для статусу.

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