Ціла мережа сайтів сповільнилася одночасно, а жоден сервер не був зайнятий.
Мережа сайтів з пошуку роботи здалася перевантаженою вся одразу. Кожен сервер простоював. Один пошук міста займав понад половину всього часу бази даних. Ось як ми його знайшли й виправили одним online-індексом.
У цій статті 8 розділів
- Кожен сайт мережі з пошуку роботи сповільнився разом з іншими, поки сервери простоювали, і перший порив був купити більші.
- Кожен рівень простоював, окрім одного: балансувальники й веб-сервери ледве працювали. Уся ферма чекала на базу даних, а база даних чекала на один запит.
- Цей запит, простий пошук міста, займав понад половину всього часу бази даних, скануючи цілу таблицю при кожному виклику, щоб повернути один рядок.
- Один online-індекс, застосований без переривання роботи сайтів, перетворив сканування на пошук за індексом. Більші сервери не знадобилися.
Мережа сайтів з пошуку роботи «просіла» в один і той самий час. Не одна сторінка, не один сайт: усі разом, той туман на всю ферму, через який оператор припускає, що сервери тонуть і час купувати більші. Це припущення дороге, воно поширене, і в цьому випадку воно було хибним. Кожна машина на фермі нудьгувала. Уся ферма чекала на базу даних, а база даних чекала на один-єдиний запит.
Кожен рівень простоював, окрім одного
При сповільненні на всю ферму спершу прочитайте завантаження кожного рівня й подивіться, який саме справді впирається у стелю. Балансувальники простоювали. Веб-сервери нудьгували. Ніщо на шляху запиту не працювало достатньо напружено, щоб пояснити повільний сайт.
- Балансувальники навантаження
- простоюють
- Веб-сервери
- ледве працюють
- База даних
- чекає на один запит
Це ознака, яка закриває суперечку про потужність ще до її початку. Якщо кожен рівень простоює, крім бази даних, більший веб-сервер купує вам швидшу простоюючу машину. Обмеженням ніколи не були обчислювальні ресурси, і жодне замовлення обладнання цього б не виправило.
Дорогою відповіддю було купити більше машин
| Інстинкт | Вимірювання |
|---|---|
| Уся ферма повільна, отже сервери перевантажені. Купіть більші. | Кожен рівень, крім бази даних, простоював. Потужність ніколи не була обмеженням. |
| ЩО СКАЗАЛИ ДАНІЦе мусить бути проблема обладнання. | Обладнання було справним, і це доведено: балансувальники й веб-сервери ледве працювали. |
| Розподіліть навантаження на більше машин. | Один запит займав понад половину всього часу бази даних. Більше машин виконували б його частіше, так само скануючи. |
Більше обладнання масштабувало б марнотратство, а не усувало його. Єдиним чесним наступним кроком було з'ясувати, на що база даних насправді витрачає свій час.
Ми агрегували історію запитів за відбитком
Застосунок рідко пише один і той самий запит однаково двічі. Той самий пошук з'являється параметризованим кількома різними способами, тож підрахунок за сирим текстом запиту ховає винуватця в шумі. Рішення: агрегувати за відбитком, який переживає цю непослідовність, а потім ранжувати кожен оператор за його часткою в загальному часі бази даних. Ми прогнали це по всій історії запитів.
-- reconstructed, de-identified rendering of cleared facts. not a real capture.
-- full query history, grouped by query_hash
-- (a fingerprint that survives inconsistent parameterisation)
SELECT TOP 5
query_hash,
SUM(execution_count) AS calls,
SUM(total_logical_reads) AS pages_read,
SUM(total_elapsed_time) * 100.0
/ SUM(SUM(total_elapsed_time)) OVER () AS pct_of_db_time
FROM sys.dm_exec_query_stats
GROUP BY query_hash
ORDER BY pct_of_db_time DESC;Один рядок вирізнявся: понад половина всього, що робила база даних, витрачалася на один оператор. Метод навмисно нудний, він не вгадує, і він щоразу однаковий.
Один запит був понад половиною бази даних
Оператор, який випав з аналізу, з тих, на які ви б ніколи не глянули двічі. Пошук міста, два стовпці, один фільтр.
-- reconstructed, de-identified. table/column names renamed from the real schema.
SELECT CityID, CityName
FROM Cities
WHERE RoutingPath = @p;
-- Execution plan: Clustered Index Scan (a full scan)
-- Returns: ONE row
-- Filtered column: RoutingPath --> no indexСтовпець, за яким він фільтрував, не мав індексу. Тож кожен виклик сканував цілу таблицю, щоб віддати один рядок. Сам по собі це малий кошт; повторений на кожному перегляді сторінки по всій мережі, він став найбільшим, що робила база даних.
Відповідь з одного рядка має читати пару сторінок, а не цілу таблицю
“Відповідь з одного рядка має читати дві-три сторінки, а не сотні.”
Це речення є всім діагнозом, стиснутим до того, що можна сказати людині, яка володіє кодом. Ніщо інше не змушує пошук одного рядка читати так багато. Не повільний диск, не зайнятий процесор, не погана ніч. Відповідь з одного рядка, що читає цілу таблицю, є повним скануванням, а повне сканування за відфільтрованим стовпцем є безпомилковою ознакою: стовпцю потрібен індекс. Нам не довелося сперечатися про обладнання, ми просто показали читання.
Один online-індекс, від сканування до пошуку
- Повне сканування таблиці заради одного рядка
- Немає індексу за відфільтрованим стовпцем
- Понад половина всього часу бази даних
- Пошук за індексом, пара сторінок заради того самого рядка
- Мала частка роботи на виклик
- Індекс створено online, без переривання роботи сайтів
-- reconstructed, de-identified. index name illustrative, on the renamed identifiers.
-- Index the column in the WHERE clause; cover the two columns it returns
CREATE NONCLUSTERED INDEX IX_Cities_RoutingPath
ON Cities (RoutingPath)
INCLUDE (CityID, CityName)
WITH (ONLINE = ON); -- applied without interrupting the site
-- New execution plan: Index SeekОдин індекс, за тим єдиним стовпцем, за яким застосунок справді фільтрував. Його застосували online, без переривання роботи сайтів, і пошук, який домінував у базі даних, став одним із найдешевших, що вона робила.
Інженер клієнта доповнив діагноз
Найкращою частиною цієї роботи був не наш індекс. А те, що сталося, коли долучився власний інженер баз даних клієнта й доповнив діагноз: він приніс шаблон блокування reader/writer зі свого боку системи. Ми об'єднали його виправлення з нашим, і обидва пішли в роботу разом.
“Інженер клієнта завершив дзвінок співавтором.”
У цьому різниця між тікетом підтримки і діагнозом. Коли знахідка правдива і ви показуєте докази, обидві сторони перестають захищати позиції й починають разом покращувати платформу.
Коли кожен рівень простоює, крім одного, перестаньте дивитися на потужність
Урок короткий і протилежний рефлексу. Повільність на всю ферму при простоюючих машинах не є проблемою потужності, а питанням до програмного забезпечення в костюмі потужності. Ось що перевірити, перш ніж підписувати замовлення на обладнання.
Спочатку прочитайте завантаження кожного рівня.
Балансувальники, веб, база даних. Якщо все простоює, крім одного рівня, потужність не є вашою проблемою, і більший сервер її не виправить.
Агрегуйте статистику запитів за відбитком.
Групуйте історію за хешем, який переживає непослідовну параметризацію, щоб той самий оператор, написаний кількома способами, рахувався як один. Найгірший винуватець зазвичай вирізняється з великим відривом.
Порівняйте прочитані сторінки з поверненими рядками.
Відповідь з одного рядка, що читає сотні сторінок, сканує, а не шукає. Цей розрив є ознакою відсутнього індексу.
Індексуйте стовпець, за яким застосунок справді фільтрує.
Не кожен стовпець. Той, що у WHERE і виконується мільйони разів. Застосуйте online, без переривання роботи сайту.
Кешуйте крихітні гарячі таблиці.
Малі довідкові таблиці, що рідко змінюються, належать до кешу, а не до критичного шляху бази даних на кожному перегляді сторінки.
Протягом усього цього машина залишалася спокійною. Балансувальники простоювали, веб-сервери нудьгували, а виправленням став один індекс за стовпцем, за яким фільтрував застосунок. Сервер бази даних був справним; одному оператору всередині нього був потрібен індекс.
Прямі відповіді
Як один запит може бути понад половиною навантаження бази даних?+
Коли запит виконується достатньо часто й робить забагато роботи щоразу, він домінує над усім іншим. Цей виконувався на кожному перегляді сторінки й сканував цілу таблицю при кожному виклику, щоб повернути один рядок, бо стовпець, за яким він фільтрував, не мав індексу. Помножте мале марнотратство на мільйони викликів, і воно стане всією базою даних.
Чому сервери простоювали, якщо сайти були повільними?+
Бо вузьке місце було всередині бази даних, що чекала на один запит. Балансувальники й веб-сервери ледве працювали. Уся ферма чекала на базу даних, а база даних чекала на пошук, який мав бути миттєвим. Додавання серверів лише додало б простоюючих машин.
Як знайти запит, який насправді є проблемою?+
Агрегуєте повну історію запитів за відбитком, який переживає непослідовну параметризацію, а потім ранжуєте за часткою в загальному часі бази даних. Метод нудний і не вгадує.
Яка ознака відсутнього індексу?+
Відповідь з одного рядка, що читає сотні сторінок. Якщо запит повертає один рядок, а план показує сканування всієї таблиці, стовпець, за яким він фільтрує, не індексований.
Чи потребувало виправлення простою?+
Ні. Індекс створили online, застосували без переривання роботи сайтів, і він перетворив сканування на пошук за індексом.
Хто це виправив, ви чи клієнт?+
Обоє, разом. Ми знайшли пошук і додали індекс; власний інженер баз даних клієнта долучився до діагнозу й приніс шаблон блокування reader/writer зі свого боку. Ми об'єднали його виправлення з нашим.
Повільний сайт, простоюючі сервери? Ми знайдемо справжню причину.
Поговоріть з інженером, який читає плани запитів, а не лише панелі, про те, що насправді сповільнює вашу платформу.
Поговоріть з інженеромІнстинкт на повільній фермі підказує відкупитися від проблеми. Чесна відповідь майже завжди дешевша: прочитайте кожен рівень, знайдіть той, що впирається у стелю, і йдіть читати його запити.
Ви дійшли до кінця.
Застосуйте прочитане на практиці
Що ви запускаєте?
Розкажіть нам про навантаження, трафік і вимоги до відновлення. Почніть з конструктора сервера або обговоріть деталі з інженером.
Написано інженерами, які будують і експлуатують інфраструктуру, про яку йдеться.Поговоріть з інженером
