Підказка NOLOCK змінює порядок повернених записів


11

На Clientполі таблиці є кластерний індекс LastName.

Коли я просто скидаю всі записи з таблиці, вони відображаються в алфавітному порядку, якщо (nolock)не використовується підказка, як у запиті, про який йдеться. Цей натяк змінює порядок записів. Повинен? Я впевнений, що жоден інший сеанс не має відкритої транзакції зі змінами цієї таблиці (принаймні sp_who2, не показує мені жодної).

Як можна пояснити різницю в порядку?

Додаткова інформація, витягнута з коментарів:

  1. Немає замовлення від. Чи повинен некластеризований індекс примусово виконувати замовлення?

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

  3. Я зробив на них WinDiff - те саме, за винятком (nolock)[натяк на запит].


21
Підказка не змінює порядок - тому що немає порядку, який можна було б змінити
a_horse_with_no_name

Відповіді:


47

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

З NOLOCKнатяком на таблицю така поведінка є розслабленою, і доступ до таблиці здійснюється в умовах більш толерантного READ UNCOMMITTEDрівня ізоляції, який може сканувати дані в порядку розподілу замість порядку індексу. Як описано в цьому посиланні, рішення про те, чи використовувати сканування порядку розподілу чи індексу, покладається на механізм зберігання даних. Цей вибір може змінюватися між виконанням без зміни плану запитів .

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

USE AdventureWorks2012;
GO
-- Appears to be ordered by BusinessEntityID
-- File:Page:Slot goes up and down several times
-- Show physical locations with sys.fn_PhysLocFormatter (undocumented)
SELECT
    P.BusinessEntityID,
    [(File:Page:Slot)] =
        sys.fn_PhysLocFormatter(%%physloc%%)
FROM Person.Person AS P;

-- Same query with TABLOCK or NOLOCK
-- Allocation-order (IAM) scan
-- Now appears to be ordered by File:Page:Slot instead of BusinessEntityID
SELECT P.BusinessEntityID,
    [(File:Page:Slot)] =
        sys.fn_PhysLocFormatter(%%physloc%%)
FROM Person.Person AS P WITH (NOLOCK);

Результати запиту

Запити запозичені з невеликою модифікацією у Пола Уайта .

Нарешті, щоб зрозуміти, ця відповідь стосується появи впорядкованого набору результатів. Немає гарантованого замовлення на презентацію без вищого рівня ORDER BY.

Сканування порядку розподілу може відбуватися за різних інших обставин, наприклад, коли придбано блокування рівня таблиці або база даних перебуває в режимі лише для читання. Паралелізм також може впливати на порядок повернення даних. Ключовим моментом є те, що без ORDER BYцього повернення даних замовлення може змінюватися в залежності від часу.


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

12

Коли я просто скидаю всі записи з таблиці

... тоді не слід очікувати жодного замовлення. Насправді один і той же запит, який виконується кілька разів, може повернутися в іншому порядку без попередження. Причина полягає в тому, що ваші два запити - це "різні" запити, швидше за все, через різний текст запиту, а не через підказку - мають різні плани виконання (і різниця може бути тонкою, наприклад, ітератор, який виглядає однаково в обидва плани, але є ordered: trueлише в одному; або значно відрізняється кількість оцінених рядків, оскільки один був складений перед суттєвою зміною даних). Також може статися сканування розпорядження про розподіл, як справедливо окреслив Джеймс у своїй відповіді.

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

Дозвольте це зрозуміти:

Якщо ви хочете або очікуєте певного замовлення, додайте пункт ЗАМОВИТИ ЗАМ

Це було розроблено раніше:

І я про це блогував:

Ось цитата останнього, яка повторює те, що я сказав, щоб звернутися до вашого коментаря про використання підказки замість ORDER BYпункту:

Навіть у випадку з натяканим індексом індекс буде використаний (якщо можливо, інакше помилка в більшості випадків), але тільки те, що використовується індекс, не означає, що результати будуть повернуті, відсортовані за ключами (клавішами) у що індекс. Вам все одно потрібно ORDER BYзабезпечити сортування за індексованими ключами.

Конор Каннінгем - досить розумний хлопець, безпосередньо відповідальний за більшу частину того, що робить SQL Server, коли він обробляє запит для вас, - також про це веде блог тут:

Будь ласка, прочитайте, а потім додайте ORDER BYдо запитів пункт, перш ніж скаржитися на різні чи несподівані сортування.


11

Джеймс чудово пояснив, як це працює, але я хотів би ще раз зазначити одне: якщо ви не використовуєте функцію замовлення, порядок рядків у наборі результатів не визначений . Якщо вам потрібне задане замовлення, використовуйте явний order byпункт - якщо ви його не вказуєте, ви, по суті, говорите "Я взагалі не переймаюся замовленням", а не "Замовити його за кластерним індексом".

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