SQL: Що сповільнює INSERT, якщо не процесор чи IO?


19

У нас є база даних для продукту, який важкий для запису. Ми щойно придбали нову серверну машину з SSD, щоб допомогти. На наш подив, вставки були не швидшими, ніж на нашій старій машині зі значно повільнішим зберіганням. Під час бенчмаркінгу ми помітили, що показник IO, демонструваний процесом SQL Server, був дуже низьким.

Наприклад, я запустив скрипт, знайдений на цій сторінці , за винятком того, що я додав BEGIN TRAN і COMMIT навколо циклу. У кращому випадку я міг бачити, як витрачання диска досягає 7 Мбіт / с, в той час як процесор ледь торкався 5%. На сервері встановлено 64 Гбіт і використовує 10. Загальний час запуску склав 2 хвилини 15 секунд для першого дзвінка до приблизно 1 хвилини для наступних дзвінків. База даних працює на простому відновленні і в ході тесту простоювала. Я кидав стіл між кожним дзвінком.

Чому такий простий сценарій так повільний? Апаратне забезпечення майже не використовується. Як спеціалізовані інструменти для порівняння дисків, так і SQLIO вказують на те, що SSD працює правильно зі швидкістю до 500 Мбіт / с як для читання, так і для запису. Я розумію, що випадкові записи проходять повільніше, ніж послідовні, але я б очікував, що така проста вставка, як ця, до таблиці без кластерної індексації, буде набагато швидшою.

Зрештою, наш сценарій набагато складніший, але я відчуваю, що мені потрібно спершу зрозуміти простий випадок. Коротше кажучи, наша програма видаляє старі дані, потім використовує SqlBulkCopy для копіювання нових даних у таблиці постановки, виконує деяку фільтрацію та, нарешті, використовує MERGE та / або INSERT INTO залежно від випадків, щоб скопіювати дані у підсумкові таблиці.

-> EDIT 1: Я дотримувався процедури, пов'язаної Мартіном Смітом, і отримав такий результат:

[Wait Type]  [Wait Count] [Total Wait (ms)] [T. Resource Wait (ms)] [T. Signal Wait (ms)]
NETWORK_IO          5008              46735                 46587        148
LOGBUFFER           901               5994                  5977         17
PAGELATCH_UP        40                866                   865          1
SOS_SCHEDULER_YIELD 53279             219                   121          98
WRITELOG            5                 145                   145          0
PAGEIOLATCH_UP      4                 58                    58           0
LATCH_SH            5                 0                     0            0

Мені здається, що дивна NETWORK_IO займає більшу частину часу, враховуючи, що немає результатів для відображення і немає даних для передачі куди-небудь, крім файлів SQL. Чи включає тип NETWORK_IO весь IO?

-> EDIT 2: Я створив диск на 20 Гб оперативної пам’яті і звідти встановив базу даних. Найкращий час, який я мав на SSD, - 48 секунд, при цьому оперативна пам’ять знизилася до 37 секунд. NETWORK_IO - все ще найбільше очікування. Максимальна швидкість запису на диск оперативної пам’яті становила близько 250 Мбіт / с, хоча він здатний робити кілька гігабайт в секунду. Він все ще не використовував багато процесора, так що ж підтримує SQL?



3
NETWORK_IOможе бути від 3 млн «1 ряду (ів) постраждалі» повідомлення відправляються назад. Ви намагалися додати SET NOCOUNT ONдо сценарію?
Мартін Сміт

Так, я додав NOCOUNT.
Джоф

2
Дивно. Тоді я взагалі не сподівався би на шляху мережевої діяльності. Ви видалили старі розширені файли подій між запусками? Сценарій, який їх читає, використовує підстановку, EE_WaitStats*.xelтому старі забруднюють ваші результати.
Мартін Сміт

Хороший дзвінок, результати оновитиму.
Джоф

Відповіді:


9

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

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

У моєму випадку потрібно тривати 36 секунд, щоб вставити 3 мільйони рядків. Це означає 36/30000000 = 0,000012 секунд у ряд. Це досить швидко. У моїй системі просто потрібно 0,000012, щоб пройти всі необхідні кроки.

Єдиний спосіб зробити це швидше - запустити другу сесію паралельно.

Якщо я розпочну 2 сеанси паралельно, обидва роблять 15 мільйонів вставок. Вони обидва закінчують за 18 секунд. Я міг би масштабувати більше, але моя поточна установка тестування вражає 95% процесор з двома паралельними сеансами, тому виконання 3 скаже результати, оскільки я потрапив у вузьке вузол процесора.

Якщо я розпочну два паралельні сеанси, обидва вставляючи 3 мільйони рядків, вони обидва закінчать за 39 секунд. так що зараз 6 мільйонів рядків за 39 секунд.

Гаразд, це все ще залишає нас з появою NETWORK_IO очікування.

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

Wait Type             Wait Count  Total Wait Time (ms) Total Resource Wait Time (ms) Total Signal Wait Time (ms)
NETWORK_IO            3455        68808                68802                         6
PAGEIOLATCH_SH        3           64                   64                            0
PAGEIOLATCH_UP        12          58                   58                            0
WRITE_COMPLETION      8           15                   15                            0
WRITELOG              3           9                    9                             0
PAGELATCH_UP          2           4                    4                             0
SOS_SCHEDULER_YIELD   32277       1                    0                             1
IO_COMPLETION         8           0                    0                             0
LATCH_SH              3           0                    0                             0
LOGBUFFER             1           0                    0                             0

Ви можете бачити, що зареєстровано 68 секунд NETWORK_IO. Але оскільки цикл вставки - це одна різьбова дія, яка займала 36 секунд, цього не може бути. (Так, використовується декілька потоків, але операції є послідовними, ніколи не паралельно, тому ви не можете накопичити більше часу очікування, ніж загальна тривалість запиту)

Якщо я не використовую розширені події, а просто статистику очікування DMV на тихому екземплярі (я просто запускаю вставку), я отримую це:

Wait Type                   Wait Count  Total Wait Time (ms)  Total Resource Wait Time (ms) Signal Resource Wait Time (ms)
SOS_SCHEDULER_YIELD             8873                 0.21                                    0.01                                    0.20
PAGEIOLATCH_UP                  3                    0.02                                    0.02                                    0.00
PREEMPTIVE_OS_AUTHENTICATIONOPS 17                   0.02                                    0.02                                    0.00
PAGEIOLATCH_SH                  1                    0.00                                    0.00                                    0.00

Таким чином, NETWORK_IO, який ви бачили в розширеному журналі подій, не був пов'язаний з вашою вставним циклом. (Якби ви не ввімкнули рахунок, у вас з'явиться масивна мережа асинхронічного очікування IO, +1 Мартін)

Однак я не знаю, чому NETWORK_IO з'являється у розширеному сліді подій. Переконайтеся, що при виписці до файлу асинхронного файлу події накопичує ASYNC_NETWORK_IO, але, безумовно, це все робиться на різному SPID, ніж на тому, за яким ми фільтруємо. Я можу сам поставити це як нове запитання)


1
"ви досягаєте межі продуктивності, не бачачи жодного вузького вузького місця, тому що ви досягли межі того, що можливо обробити протягом одного сеансу одним потоком": ви описуєте 100% вузьке місце процесора (на одному ядрі). Якщо немає вузьких місць, то система буде йти швидше, так що- то ще має бути в грі.
Рем Русану

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

9

Як правило , ви починаєте дивитися на sys.dm_exec_requests, в зокрема , на wait_time, wait_typeі wait_resourceдля запиту INSERT (ів). Це дасть чітку інформацію про те, що блокує ваш ВСТУП. Результати будуть вказувати, чи є суперечка щодо блокування, події росту файлів, очікування змивання журналу, суперечка розподілу (проявляється як суперечка засувки на сторінці PFS) тощо тощо тощо. Після вимірювання оновіть відповідне запитання відповідно. Я настійно закликаю вас зупинитися зараз і прочитати методологію усунення несправностей " Чекання та черги", перш ніж продовжувати.


3

Я запустив тестовий скрипт на сторінці, пов’язаній в ОП з BEGIN TRAN / COMMIT навколо циклу. На моїй машині вперше пройшло 1:28.

Потім я перемістив ці дві команди поза циклом:

SELECT @Random = ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0)
SET @InsertDate = DATEADD(dd, @Random, GETDATE())

Він завершився через 28 секунд після цього.

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

FWIW, SSD не завжди є найкращою технологією для важких додатків. Для кращої продуктивності переконайтеся, що ваш журнал БД знаходиться на іншій букві диска від даних БД, файл журналу попередньо зростав до максимального розміру, і ніколи не обрізайте журнал.


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

1

Ще один DMV, який я використовую для виявлення повільності, - це sys.dm_os_waiting_tasks . Якщо ваш запит не є інтенсивним процесором, ви можете знайти більше інформації про очікування від цього DMV.


0

Я перевіряю список подій очікування на sql 2008, і я не бачу в списку NETWORK_IO: http://technet.microsoft.com/en-us/library/ms179984(v=sql.100).aspx

Я подумав, що NETWORK_IO тепер просто вказаний як ASYNC_NETWORK_IO, тому я хотів запитати, чи можете ви ще раз перевірити свою версію SQL, тому що мені просто цікаво, як / чому з’являється ця подія очікування для цієї версії.

Що стосується того, що мережеве очікування з’являється взагалі, так, це може статися, навіть якщо ви працюєте на автономному сервері. Ви перевірили налаштування мережевих карт? Мені цікаво, чи вони є проблемою.

Зрештою, можливі лише вузькі місця ресурсів: пам'ять, процесор, введення / виведення диска, мережа та блокування. Ви вказали, що процесор і введення / виведення не є проблемою, і у вас є подія очікування NETWORK_IO, тому я пропоную вам спочатку переглянути ці картки NIC.


1
NETWORK_IOПоказано , тому що OP використовує розширені події. Він ніколи не оновлювався вsys.dm_xe_map_values
Мартін Сміт,

Я думаю про той самий SQLRockstar, про те, що може бути далі. Я намагався повністю відключити мережеві карти. Мартін зазначив, що деякі старі файли, можливо, ще є, я оновлю результати, щоб побачити, чи щось змінить.
Джоф

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