Унікальні лічильники оновлень індексів та статистичних рядків


14

З огляду на наступну таблицю, унікальний кластерний індекс та статистику:

CREATE TABLE dbo.Banana
(
    pk integer NOT NULL, 
    c1 char(1) NOT NULL, 
    c2 char(1) NOT NULL
);

CREATE UNIQUE CLUSTERED INDEX pk ON dbo.Banana (pk);

CREATE STATISTICS c1 ON dbo.Banana (c1);
CREATE STATISTICS c2 ON dbo.Banana (c2);

INSERT dbo.Banana 
    (pk, c1, c2) 
VALUES 
    (1, 'A', 'W'), 
    (2, 'B', 'X'), 
    (3, 'C', 'Y'), 
    (4, 'D', 'Z');

-- Populate statistics
UPDATE STATISTICS dbo.Banana;

Приклад даних

Лічильники зміни рядків статистики очевидно показують нуль перед будь-якими оновленнями:

-- Show statistics modification counters
SELECT
    stats_name = S.[name], 
    DDSP.stats_id,
    DDSP.[rows],
    DDSP.modification_counter
FROM sys.stats AS S
CROSS APPLY sys.dm_db_stats_properties(S.object_id, S.stats_id) AS DDSP
WHERE
    S.[object_id] = OBJECT_ID(N'dbo.Banana', N'U');

Нульові лічильники модифікації

Збільшення pkзначення кожного стовпця по одному для кожного рядка:

-- Increment pk in every row
UPDATE dbo.Banana 
SET pk += 1;

Використовується план виконання:

План сортування Сортувати план виконання

Він виробляє такі лічильники модифікації статистики:

Лічильники модифікацій після оновлення

Запитання

  1. Що роблять оператори Split, Sort і Collapse?
  2. Чому pkстатистика показує 2 модифікації, але c1і c2показати 5?

Відповіді:


15

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

Опрацьовуючи приклад у запитанні, ми можемо записати оновлення як окреме однорядне оновлення для кожного з чотирьох присутніх рядків:

-- Per row updates
UPDATE dbo.Banana SET pk = 2 WHERE pk = 1;
UPDATE dbo.Banana SET pk = 3 WHERE pk = 2;
UPDATE dbo.Banana SET pk = 4 WHERE pk = 3;
UPDATE dbo.Banana SET pk = 5 WHERE pk = 4;

Проблема полягає в тому, що перше твердження буде невдалим, оскільки воно змінюється pkз 1 на 2, і вже є рядок, деpk = 2. Система зберігання SQL Server вимагає, щоб унікальні індекси залишалися унікальними на кожному етапі обробки, навіть у межах одного оператора . Це проблема, яку вирішили розділити, сортувати та згортати.

Розкол Розкол

Перший крок - розділити кожну операцію оновлення на видалення, а потім вставити:

DELETE dbo.Banana WHERE pk = 1;
INSERT dbo.Banana (pk, c1, c2) VALUES (2, 'A', 'W');

DELETE dbo.Banana WHERE pk = 2;
INSERT dbo.Banana (pk, c1, c2) VALUES (3, 'B', 'X');

DELETE dbo.Banana WHERE pk = 3;
INSERT dbo.Banana (pk, c1, c2) VALUES (4, 'C', 'Y');

DELETE dbo.Banana WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');

Оператор Split додає стовпчик коду дії до потоку (тут позначений Act1007):

Спліт властивості

Код дії - 1 для оновлення, 3 для видалення та 4 для вставки.

Сортувати Сортувати

Розподілене твердження вище все ще призведе до помилкового перехідного унікального порушення ключа, тому наступним кроком є ​​сортування висловлювань за ключами унікального індексу, що оновлюється ( pkу цьому випадку), а потім за кодом дії. У цьому прикладі це просто означає, що видалення (3) на одному ключі впорядковується перед вставками (4). Отримане замовлення:

-- Sort (pk, action)
DELETE dbo.Banana WHERE pk = 1;
DELETE dbo.Banana WHERE pk = 2;
INSERT dbo.Banana (pk, c1, c2) VALUES (2, 'A', 'W');
DELETE dbo.Banana WHERE pk = 3;
INSERT dbo.Banana (pk, c1, c2) VALUES (3, 'B', 'X');
DELETE dbo.Banana WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (4, 'C', 'Y');
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');

Сортування властивостей

Згорнути Згорнути

Попереднього етапу достатньо для гарантування уникнення помилкових порушень унікальності у всіх випадках. В якості оптимізації Collapse поєднує в оновлення суміжні вилучення та вставки одного і того ж ключового значення :

-- Collapse (pk)
DELETE dbo.Banana WHERE pk = 1;
UPDATE dbo.Banana SET c1 = 'A', c2 = 'W' WHERE pk = 2;
UPDATE dbo.Banana SET c1 = 'B', c2 = 'X' WHERE pk = 3;
UPDATE dbo.Banana SET c1 = 'C', c2 = 'Y' WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');

Пари видалення / вставки для pkзначень 2, 3 і 4 об'єднали в оновлення, залишивши одне видалення на pk= 1, а вставку для pk= 5.

Оператор Collapse згрупує рядки за ключовими стовпцями та оновлює код дії для відображення результату згортання:

Згорнути властивості

Кластерне оновлення індексу Кластерне оновлення індексу

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

Кластеризований властивість індексу оновлення


Рядки модифікації лічильників

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

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

Отже, лічильники модифікацій рядків рядків показують 2 зміни pkта 5 для c1та c2:

-- Collapse (pk)
DELETE dbo.Banana WHERE pk = 1;                         -- All columns modified
UPDATE dbo.Banana SET c1 = 'A', c2 = 'W' WHERE pk = 2;  -- c1 and c2 modified
UPDATE dbo.Banana SET c1 = 'B', c2 = 'X' WHERE pk = 3;  -- c1 and c2 modified
UPDATE dbo.Banana SET c1 = 'C', c2 = 'Y' WHERE pk = 4;  -- c1 and c2 modified
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');    -- All columns modified

Примітка: Тільки зміни, застосовані до базового об'єкта (купи або кластерний індекс), впливають на лічильники модифікації рядків рядків статистики. Некластеризовані індекси є вторинними структурами, що відображають зміни, вже внесені до базового об'єкта. Вони взагалі не впливають на лічильники модифікації рядків статистики.

Якщо об’єкт має кілька унікальних індексів, для організації оновлень до кожного використовується окрема комбінація розділення, сортування, згортання. SQL Server оптимізує цей випадок для некластеризованих індексів, зберігаючи результат розділення на котушку Eager Table, а потім відтворюючи цей набір для кожного унікального індексу (який буде мати власний сортування за індексними ключами + код дії та згортання).

Вплив на оновлення статистики

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

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

У наведеному вище прикладі зміни рядків для стовпчика ключів збільшуються на 2 (зміна чистої), а не на 4 (по одній для кожного порушеного рядка таблиці) або на 5 (по одній для кожного видалення / оновлення / вставки, виробленого згортанням).

Крім того, неклавішні стовпці, які логічно не були змінені оригінальним запитом, накопичують модифікації рядків, які можуть налічувати аж двічі оновлених рядків таблиці (один для кожного видалення та один для кожної вставки).


Кількість зафіксованих змін залежить від ступеня перекриття між старими та новими значеннями стовпців ключових (і тому ступінь, на яку можна згортати окремі видалення та вставки). Скидання таблиці між кожним виконанням, наступні запити демонструють вплив на лічильники модифікації рядків з різними перекриттями:

UPDATE dbo.Banana SET pk = pk + 0; -- Full overlap

pk = pk + 0

UPDATE dbo.Banana SET pk = pk + 1;

pk = pk + 1

UPDATE dbo.Banana SET pk = pk + 2;

pk = pk + 2

UPDATE dbo.Banana SET pk = pk + 3;

pk = pk + 3

UPDATE dbo.Banana SET pk = pk + 4; -- No overlap

pk = pk + 4

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