Чому друге INSERTтвердження ~ 5x повільніше першого?
З огляду на кількість створених даних журналу, я думаю, що другий не відповідає мінімальному журналу. Однак документація в Посібнику з продуктивності завантаження даних вказує на те, що обидві вставки повинні бути спроможні мінімально реєструватися. Отже, якщо мінімальна реєстрація є ключовою різницею продуктивності, чому так, що другий запит не відповідає мінімальному журналу? Що можна зробити, щоб покращити ситуацію?
Запит №1: Вставка рядків 5 ММ за допомогою INSERT ... WITH (TABLOCK)
Розглянемо наступний запит, який вставляє 5 мм рядків у купу. Цей запит виконує 1 secondта генерує 64MBдані журналу транзакцій, як повідомляється sys.dm_tran_database_transactions.
CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that correctly estimates that it will generate 5MM rows
FROM dbo.fiveMillionNumbers
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO
Запит №2: Вставка одних і тих же даних, але SQL занижує # рядок
Тепер розглянемо цей дуже схожий запит, який працює за точно такими ж даними, але трапляється з таблиці (або складної SELECTзаяви з багатьма об'єднаннями в моєму фактичному виробничому випадку), де оцінка кардинальності занадто низька. Цей запит виконує 5.5 secondsта генерує 461MBдані журналу транзакцій.
CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that produces 5MM rows but SQL estimates just 1000 rows
FROM dbo.fiveMillionNumbersBadEstimate
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO
Повний сценарій
Дивіться в цій пастебі повний набір сценаріїв для генерування тестових даних та виконання будь-якого з цих сценаріїв. Зауважте, що ви повинні використовувати базу даних, яка є у SIMPLE моделі відновлення .
Бізнес-контекст
Ми напівчасто пересуваємося мільйонами рядків даних, і важливо, щоб ці операції були максимально ефективними, як з точки зору часу виконання, так і з навантаження дискового вводу / виводу. Спочатку у нас було враження, що створення таблиці куч і використання INSERT...WITH (TABLOCK)- це хороший спосіб зробити це, але тепер стали менш впевненими, враховуючи, що ми спостерігали ситуацію, продемонстровану вище, у фактичному виробничому сценарії (хоча і зі складнішими запитами, а не тут спрощена версія).
SELECTзаява з численними приєднаннями, яка генерує набір результатів дляINSERT. Ці об'єднання дають погані оцінки кардинальності для оператора вставки кінцевої таблиці (який я імітував у сценарії repro через поганийUPDATE STATISTICSвиклик), і тому це не так просто, як видаватиUPDATE STATISTICSкоманду для усунення проблеми. Я повністю погоджуюся, що спрощення запиту таким чином, щоб Оцінювач кардинальності легше зрозумів, може бути хорошим підходом, але реалізація заданої складної бізнес-логіки не є дрібницею.