Шукати індекси проти сканування індексів


64

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

У чому різниця між пошуком та індексом та скануванням індексів?

Що краще?

Як SQL обирає один над іншим?

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


6
У вас є хороша довідка щодо використання luke use-the-index-luke .
Мар’ян

7
Не всі сканування є поганими - іноді це найефективніший спосіб задовольнити запит. Також зауважте, що не всі шукачі - це шукання - часто це фактично сканування дальності, а пошук лише вказує, як він дійшов до початку діапазону.
Аарон Бертран

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

Відповіді:


76

Коротка версія: шукати набагато краще

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

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

Сканування таблиць: Без індексів, що стосуються вашого запиту, планувальник змушений використовувати сканування таблиці, що означає, що кожен рядок переглядається. Це може призвести до того, що кожна сторінка, що стосується даних таблиці, зчитується з диска, що є найгіршим випадком. Зауважте, що для деяких запитів він використовуватиме сканування таблиці навіть тоді, коли є корисний індекс - це, як правило, тому, що даних у таблиці настільки мало, що обмінювати індекси складніше (якщо це так, то можна було б очікувати плануєте змінитись у міру зростання даних, вважаючи, що міра вибірковості індексу є хорошою).

Сканування покажчиків з пошуку рядків: не знайдено індексу, який можна безпосередньо використати для пошуку, але індекс, що містить правильні стовпці, не може бути використаний. Наприклад, якщо у вас є велика таблиця з 20 стовпцями з індексом на колонки1, col2, col3, і ви видаєте SELECT col4 FROM exampletable WHERE col2=616, у цьому випадку сканування індексу на запит col2краще, ніж сканування всієї таблиці. Після того, як будуть знайдені відповідні рядки, тоді сторінки даних потрібно прочитати для вибору col4 для виведення (або подальшого приєднання), що є етапом "пошуку закладок", коли ви бачите це в планах запитів.

Сканування покажчиків без пошуку рядків: Якщо вищенаведений приклад, SELECT col1, col2, col3 FROM exampletable WHERE col2=616тоді додаткові зусилля для читання сторінок даних не потрібні: як col2=616тільки знайдеться відповідність рядків індексу , всі необхідні дані будуть відомі. Ось чому ви іноді бачите стовпці, за якими ніколи не буде здійснюватися пошук, але, ймовірно, буде потрібно запит на вихід, доданий до кінця індексів - це може зберегти пошук рядків. Додаючи стовпці до індексу лише з цієї причини, додайте їх із INCLUDEзастереженням, щоб сказати двигуну, що не потрібно оптимізувати макет індексу для запитів на основі цих стовпців (це може пришвидшити оновлення цих колонок) . Сканування покажчиків може бути результатом запитів, у яких відсутні фільтрувальні пропозиції: SELECT col2 FROM exampletableсканувати цей приклад індексу замість сторінок таблиці.

Шукає індекс (із пошуковими рядками рядків або без них) : при пошуку не враховується весь індекс. Для SELECT * FROM exampletable WHERE c1 BETWEEN 1234 AND 4567запиту двигун запиту може знайти перший рядок, який буде відповідати, виконуючи пошук на основі дерева по індексу, c1потім він може переміщатись по індексу, поки не дістанеться до кінця діапазону (це те саме, що і з запитом бо c1=1234може бути багато рядків, що відповідають умові навіть для =операції). Це означає, що замість кожної сторінки в індексі (або таблиці) потрібно читати лише відповідні сторінки покажчиків (плюс декілька, необхідні для початкового пошуку).

Кластерні індекси: із кластерним індексом дані таблиці зберігаються у вузлах листків цього індексу, а не в окремій структурі купи. Це означає, що після пошуку рядків за допомогою цього індексу ніколи не знадобиться зайвих пошукових рядків, незалежно від того, які стовпці потрібні [якщо ви не маєте сторонніх даних, таких як TEXTстовпці або VARCHAR(MAX)стовпці, що містять довгі дані].

З цієї причини у вас може бути лише один кластерний індекс [1] , кластерний індекс - це ваша таблиця, замість того, щоб мати окрему структуру купи, тож якщо ви використовуєте один [2], вибирайте те, куди ви ставите його обережно, щоб отримати максимальний прибуток.

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

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

[2] Коли я говорю «якщо ви використовуєте кластерний індекс», не відзначити , що , як правило , рекомендується робити один на кожному столі. Існують винятки, як і у всіх правилах роботи з таблицями, на яких не бачать нічого, крім об'ємних вставок і невпорядкованих зчитувань (можливо, таблиць постановки для процесів ETL), є найпоширенішим прикладом лічильника.

Додатковий пункт: неповні сканування:

Важливо пам’ятати, що залежно від решти запиту сканування таблиці / індексу може насправді не сканувати всю таблицю - якщо логіка дозволяє, щоб план запитів міг спричинити її переривання рано. Найпростіший приклад цього SELECT TOP(1) * FROM HugeTable- якщо ви подивитесь на план запитів, ви побачите, що зі сканування повернуто лише один рядок, і якщо ви переглянете статистику IO ( SET STATISTICS IO ON; SELECT TOP(1) * FROM HugeTable), ви побачите, що він читає лише дуже невелике число сторінок (можливо, лише одна).

Те ж може статися, якщо присудок WHEREабо JOIN ... ONпункт може виконуватися одночасно зі скануванням, яке є джерелом, якщо його дані. Планувач запитів / бігун іноді може бути дуже розумним щодо відштовхування предикатів назад до джерел даних, щоб дозволити таким чином дострокове припинення сканування (а іноді ви можете бути розумними в перестановці запитів, щоб допомогти це зробити!). У той час як дані протікають справа наліво відповідно до стрілок на екрані стандартного плану запитів, логіка працює ліворуч праворуч і кожен крок (справа наліво) не обов'язково виконується до завершення, перш ніж наступний може розпочатися. У наведеному вище простому прикладі, якщо ви дивитесь на кожен блок плану запитів як агент, SELECTагент запитує у TOPагента рядок, який у свою чергу запитуєTABLE SCANагент для одного, тоді SELECTагент запитує іншого, але TOPагент знає, що немає потреби, не турбується навіть запитувати читач таблиці, SELECTагент отримує відповідь "не більше актуально" і знає, що вся робота виконана. Багато операцій блокують цей вид оптимізації , звичайно , так часто і в більш складних прикладах таблиці / індекс сканування дійсно чи читати кожен рядок, але будьте обережні , щоб не перейти до висновку , що будь-яка перевірка повинна бути дорогою операцією.


6

Як правило, прагнення хороші, сканування - погані.

Шукає, де запит може ефективно використовувати індекс, і використовує його для пошуку потрібних йому рядків.

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

Як вибирає SQL? Заглиблений у внутрішній оптимізатор запитів, рішення приймається на основі вашого запиту та наявних індексів та статистичної інформації, пов’язаної з цими індексами.

Тут можна прочитати кілька книг, які можуть зацікавити - Обидві книгарні Red-Gate за адресою http://www.red-gate.com/community/books/

  • Плани виконання SQL Server від Гранта Фрітчі
  • Всередині оптимізатора запитів Бенджаміна Невареса
  • Статистика SQL Server Холгера Шмелінга

7
Для того ж плану добре сканувати одну таблицю, мільйон шукає - погано. Тож ваше перше твердження не зовсім правильне.
Мар’ян

Дійсно, пошук індексів та сканування індексів у кожного є власним використанням, не можна сказати, що кращий за інший БЕЗ контексту базових таблиць та запитів. У більшості випадків, якщо в таблиці є неточна статистика, план виконання може вийти як неоптимальний, наприклад, пошук індексу помилково вибирається під час сканування індексу та навпаки.
jyao

5

Якщо ви хочете розкопати тему, дуже корисною книгою (принаймні для мене) є Плани виконання SQL Server від Гранта Фрітчі, вільно доступні в RedGate тут .

Якщо у вас є запит типу

SELECT *
FROM myTable

SQL Server, ймовірно, використовуватиме сканування індексів, оскільки йому потрібно пройти всі рядки для відображення необхідних результатів.

Навпаки,

SELECT *
FROM myTable
WHERE myID = 1

неодмінно призведе до пошуку індексу. SQL Server використовуватиме B- деревову структуру індексу myID, і отримання потрібного рядка буде набагато швидшим.


Я не знаю, чи згоден я з "звичайно" - навіть якщо індекс має мій ID як провідний стовпець, пошук може не бути оптимальною відповіддю (залежить від багатьох речей, наприклад, унікальний він - який може бути вірно в таблиці клієнтів, але не для ідентифікатора клієнта в таблиці замовлень, скільки стовпців потрібно покрити, але їх немає в індексі тощо).
Аарон Бертран

Я не думаю, що ця відповідь дійсно охоплює поставлені питання.
Нуль3

5

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

Але, наприклад, скажімо, що ваш код запитує на стовпці А та стовпці Б на заданих фільтрах, але ви також хочете повернути значення стовпців С та стовпця Е, ви можете створити індекс у стовпцях А та В за допомогою ВКЛЮЧЕННЯ опція, що містить стовпці С та Е. Таким чином, пошук одного індексу поверне все необхідне, оскільки не потрібно робити пошук, щоб отримати інші значення (C і E) в одному рядку.

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