Як можна покращити оцінки рядків, щоб зменшити шанси на розливання до tempdb


11

Я зауважую, що, коли трапляються події tempdb події (спричиняючи повільні запити), часто оцінки рядків відключаються для певного об'єднання. Я бачив, як події розливу відбуваються під час злиття та приєднання хешу, і вони часто збільшують тривалість виконання від 3 до 10 разів. Це питання стосується того, як поліпшити кошториси рядків, припускаючи, що це зменшить шанси на розлив подій.

Фактична кількість рядків 40 к.

Для цього запиту план показує неправильну оцінку рядків (11,3 рядків):

select Value
  from Oav.ValueArray
 where ObjectId = (select convert(bigint, Value) NodeId
                     from Oav.ValueArray
                    where PropertyId = 3331  
                      and ObjectId = 3540233
                      and Sequence = 2)
   and PropertyId = 2840
option (recompile);

Для цього запиту план показує хорошу оцінку рядків (56k рядків):

declare @a bigint = (select convert(bigint, Value) NodeId
                       from Oav.ValueArray
                      where PropertyId = 3331
                        and ObjectId = 3540233
                        and Sequence = 2);

select Value
  from Oav.ValueArray
 where ObjectId = @a               
   and PropertyId = 2840
option (recompile);

Чи можна додати статистику чи підказки для покращення оцінок рядків у першому випадку? Я спробував додати статистику з певними значеннями фільтра (властивість = 2840), але або не вдалося отримати правильну комбінацію, або, можливо, її ігнорують, оскільки ObjectId невідомий під час компіляції, і він може вибирати середнє значення для всіх ObjectIds.

Чи є режим, коли він спочатку здійснить запит зонда, а потім використає його для визначення оцінок рядків чи він повинен сліпо пролетіти?

Ця властивість має багато значень (40 к) для кількох об'єктів і нуль у переважній більшості. Я був би радий натяком, де можна було б вказати максимальну очікувану кількість рядків для заданого з'єднання. Це, як правило, непроста проблема, оскільки деякі параметри можуть визначатися динамічно як частина з'єднання або краще розміщуватись у представленні (немає підтримки змінних).

Чи є якісь параметри, які можна скорегувати, щоб мінімізувати ймовірність розливу до tempdb (наприклад, мінімум пам'яті на запит)? Надійний план не вплинув на кошторис.

Редагувати 2013.11.06 : Відповідь на коментарі та додаткову інформацію:

Ось зображення плану запитів. Попередження про кардинальність / шукати предикат з convert ():

введіть тут опис зображення введіть тут опис зображення

За коментарем @Aaron Bertrand я спробував замінити convert () як тест:

create table Oav.SeekObject (
       LookupId bigint not null primary key,
       ObjectId bigint not null
);

insert into Oav.SeekObject (
   LookupId, ObjectId
) VALUES (
   1, 3540233
) 

select Value
  from Oav.ValueArray
 where ObjectId = (select ObjectId 
                     from Oav.SeekObject 
                    where LookupId = 1)
   and PropertyId = 2840
option (recompile);

введіть тут опис зображення

Як дивна, але успішна цікава точка, також дозволила коротко замикати пошук:

select Value
  from Oav.ValueArray
 where ObjectId = (select ObjectId 
                     from Oav.ValueArray
                    where PropertyId = 2840
                      and ObjectId = 3540233
                      and Sequence = 2)
   and PropertyId = 2840
option (recompile);

введіть тут опис зображення

Обидва перелічують правильний пошук ключів, але лише перший містить "Вихід" ObjectId. Я здогадуюсь, що вказує на те, що другий справді короткий замикання?

Чи може хтось перевірити, чи виконуються однорядні зонди, щоб допомогти з оцінкою рядків? Очевидно, що оптимізація обмежується лише оцінкою гістограми, коли однорядний пошук ПК може значно підвищити точність пошуку в гістограмі (особливо, якщо є потенціал розливу чи історія). Коли в цих реальних запитах є 10 цих підпоєднань, в ідеалі вони відбуватимуться паралельно.

Побічна примітка, оскільки sql_variant зберігає базовий тип (SQL_VARIANT_PROPERTY = BaseType) у самому полі, я б очікував, що перетворення () буде майже безцінним, доки воно "прямо" конвертоване (наприклад, не рядкове в десяткове, а швидше int до int або, можливо, int to bigint). Оскільки це невідомо під час компіляції, але може бути відомий користувачеві, можливо, функція "AssumeType (тип, ...)" для sql_variants дозволила б обробляти їх більш прозоро.


1
Перша здогадка полягала б у тому, що перетворення на bigint скидає ваші оцінки (план запитів буде попереджати про це у SQL Server 2012), але з іншого боку, ваш підзапит не може повернути нічого, крім 0 або 1 рядків для успішного запиту. Було б цікаво побачити у вас плани запитів. Можливо, як посилання на версію XML.
Мікаель Ерікссон

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

2
Яка версія SQL Server? Чи можете ви надати таблиці та індекси DDL та статистичні краплі (одно- та багатоколонкові) для таблиць, щоб ми могли побачити деталі проблеми? Розбиття запиту за допомогою, declare @a bigint = як ви це зробили, здається для мене природним рішенням, чому це неприйнятно?
Пол Білий 9

2
Я думаю, що ваш дизайн - це (дуже спрощений) дизайн EAV, який змушує вас використовувати CONVERT()в стовпцях, а потім приєднуватися до них. Це, звичайно, неефективно в більшості випадків. У цьому конкретному конвертується лише одне значення, тому, ймовірно, це не проблема, але які індекси у вас в таблиці? Проекти EAV зазвичай працюють добре, лише за умови належної індексації (що означає багато індексів у вузьких таблицях).
ypercubeᵀᴹ

@Paul White, що стосується розколу ... це чудово як рішення для цієї справи. Але для більш загального / складного я здебільшого не хочу відмовлятися від паралелізації та читабельності. Скажімо, у мене є 10 таких підзапитів (деякі є більш складними) у запиті, але лише 5 потрібно "дозріти", перш ніж решта запиту може початися - хотілося б уникнути необхідності з'ясувати, які з них 5.
crokusek

Відповіді:


7

Я не буду коментувати розливи, tempdb або підказки, тому що запит здається досить простим, тому потребує такого розгляду. Я думаю, що оптимізатор SQL-сервера зробить свою роботу досить добре, якщо для запиту є індекси.

І ваше розбиття на два запити добре, оскільки показує, які індекси будуть корисні. Перша частина:

(select convert(bigint, Value) NodeId
 from Oav.ValueArray
 where PropertyId = 3331  
   and ObjectId = 3540233
   and Sequence = 2)

потрібен індекс на (PropertyId, ObjectId, Sequence)включення Value. Я б зробив це UNIQUEв безпеці. Запит буде інакше помилятись під час виконання, якщо було повернуто більше одного рядка, тому добре заздалегідь переконатися, що цього не відбудеться, за допомогою унікального індексу:

CREATE UNIQUE INDEX
    PropertyId_ObjectId_Sequence_UQ
  ON Oav.ValueArray
    (PropertyId, ObjectId, Sequence) INCLUDE (Value) ;

Друга частина запиту:

select Value
  from Oav.ValueArray
 where ObjectId = @a               
   and PropertyId = 2840

потрібен індекс про (PropertyId, ObjectId)включення Value:

CREATE INDEX
    PropertyId_ObjectId_IX
  ON Oav.ValueArray
    (PropertyId, ObjectId) INCLUDE (Value) ;

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

У такому випадку перетворення (необхідні для проектування EAV та зберігання різних типів даних в одних стовпцях) є ймовірною причиною, і ваше рішення розщеплення (як @AAron Bertrand та @Paul White) запит видається природним і шлях. Модифікація, щоб мати різні типи даних у відповідних стовпцях може бути іншим.


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

5

Як часткова відповідь на явне запитання щодо вдосконалення статистики ...

Зауважте, що оцінки рядків навіть для розділеного окремого випадку все ще вимикаються на 10 разів (4 к проти очікуваних 40 к).

Гістограма статистики, ймовірно, була надто тонкою для цієї властивості, оскільки це довга (вертикальна) таблиця з рядками 3,5 М, і ця особливість є надзвичайно рідкою.

Створіть додаткову статистику (дещо зайву зі статистикою IX) для розрідженого властивості:

create statistics [STAT_ValueArray_ObjPropValue_WhereProp2840] ON [Oav].[ValueArray](ObjectId, PropertyId, Value)
where PropertyId = 2840

Оригінали:

введіть тут опис зображення введіть тут опис зображення

З конвертацією () видалено (належним чином):

введіть тут опис зображення

З конвертом () видалено (коротке замикання):

введіть тут опис зображення

Вимкнено на ~ 2X, ймовірно, тому що> 99,9% об'єктів взагалі не визначають властивість 2840. Насправді саме для цього тестового випадку властивість існує лише на 1 з 200 тис. Різних об'єктів таблиці 3.5M рядків. Це дивовижно, що він настільки близький. Регулюючи фільтр таким чином, щоб він був меншим,

create statistics [STAT_ValueArray_ObjPropValue_WhereProp2840] ON [Oav].[ValueArray](ObjectId, PropertyId, Value)
where PropertyId = 2840 and ObjectId >= 3540000 and ObjectId < 3541000

create statistics [STAT_ValueArray_ObjPropValue_WhereProp2840] ON [Oav].[ValueArray](ObjectId, PropertyId, Value)
where PropertyId = 2840 and ObjectId = 3540233;

Хм, ніяких змін ... Резервне копіювання додано "з повним скануванням" до кінця статистики (можливо, попередні два не спрацювали) та так:

create statistics [STAT_ValueArray_ObjPropValue_WhereProp2840] ON [Oav].[ValueArray](ObjectId, PropertyId, Value)
where PropertyId = 2840 with full scan;

введіть тут опис зображення

Так. Так, у високо вертикальній таблиці з широко висвітленим IX, додавання додаткової відфільтрованої статистики представляється великим поліпшенням (особливо для рідкісних, але дуже варіативних комбінацій клавіш).


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