Поводом написать эту статью стал весьма достойный обзор Как мы тестировали VMware vSAN... компании КРОК. Обзор-то достойный, но в нем есть фраза, с которой я борюсь уже больше десятка лет. Админы СХД, виртуализаторы и интеграторы раз за разом повторяют: "Задержки в 5 мс — это отличный показатель". Даже цифра в 5 мс десять лет не меняется. Я это слышал вживую от весьма уважаемых админов уже не меньше десятка раз. От менее уважаемых — десятки, а уж сколько раз читал в интернете… Нет, нет, нет. Для OLTP нагрузок 5 мс, особенно так, как их обычно измеряют — это epic fail. Мне приходилось объяснять причины этого уже много раз, на этот раз я решил собрать свои мысли в переиспользуемую форму.
Сразу оговорюсь, что в упомянутой выше статье этих ошибок нет, скорее фраза сработала как триггер.
Типичное начало
Всё что описано в данной статье, верно для распространенных СУБД, используемых для типичной бизнесовой OLTP. Больше всего у меня опыт с MS SQL Server, но, как минимум, для PostgeSQL, Oracle и Sybase многие моменты и выводы также останутся верны.
Производительностью СУБД обычно недовольны все. Если в крупной системе есть СУБД — а она, внезапно, почти всегда есть — то эта СУБД и есть узкое место. Ну или сразу станет узким местом, если начать оптимизировать всё остальное. И вот, приходит заказчик и говорит человеческим голосом: "Помоги! Спаси! Заплатили $NNNNNNNN за сервера и СХД, а скорость не растет! Уж и администратор настраивал и вендор консультировал, а всё равно не шевелится." Если разработчики системы подходят под определение Лаврова (обойдёмся без точной цитаты), а специалисты эксплуатации и сопровождения "борются с инцидентами перезагрузкой сервера", то часто и проблема простая и незатейливая: отсутствуют индексы, кривые запросы, фатальные ошибки настройки (про которые в документации жирным написано "так делать нельзя!!!"), излишние блокировки, взаимоблокировки и прочая простая и понятная ерунда. Таких случаев много, большинство, но не все. Если система в сложности или нагрузке перешла некоторый невидимый предел, то она либо сдохнет от этих проблем, либо перейдёт на следующий уровень.
IMHO, лучшим средством сейчас является SQL Server First Responder Kit, продвигаемый Брентом Озаром. Этот инструмент развивается весьма активно. Еще есть достойный набор от Гленна Берри, он тоже не забросил свой проект. Оба набора по-своему прекрасны, чтение комментариев и запросов в первый раз открывает много нового. Я сам всегда начинаю осматриваться с sys.dm_os_waitsats
, беглого взгляда на Error log и выяснения, есть ли хоть сколько-то работающая система резервного копирования.
На этом уровне уже сервер не стоит под столом директора, диски уже не внутри сервера, а в СХД, разработчики знают про индексы, а администраторы уже умеют PowerShell, и менеджеры ИТ начинают говорить умные слова типа SLA и RPO/RTO. Вот на этом уровне возникает интересная ситуация:
- СУБД является узким местом.
- Сервер вроде бы по всем показателям должен быть достаточен.
- Программно СУБД дальше улучшать можно, но сложно (либо переходить на более дорогие лицензии, либо переходить в "красную зону кривой им. Шипилёва" по оптимизации)
- Дисковая система куплена дорогая и, вроде даже как-то настроена.
Ан нет. Крокодил не ловится, не растет кокос, и производительность системы такая же или ниже чем на старом сервере. Смотрю в sys.dm_os_waitsats
и вижу WRITELOG
, PAGEIOLATCH_SH
и PAGEIOLATCH_EX
в топе, среднее время ожидания 5+ мс. Ну типично, чо: "Эй, админы и DBA, тут у вас дисковая система — bottleneck" и вот тут начинается старая песня про 5 мс:
- У нас 5 мс по SLA
- Да у нас полка 20000 IOPS тащит
- Нам вендор сказал, что все файлы БД можно на один раздел
- У нас виртуализация и гиперконвергентность и мы не можем выделять отдельные диски под БД
- По нашим данным утилизация сервера 5%
- Всё настроено по рекомендациям
- Вашим БД не нужна большая производительность, она не делает больше 300 IOPS (а у нас же полка на 20000 IOPS)
Кстати, всё вышесказанное не только про "свои" сервера, но и про облачные сервисы и виртуализацию. Там есть кучка своей специфики, но типичная клиническая картина примерно та же: в меру оптимизированная БД, неглупые сотрудники разработки и сопровождения, по процессору и памяти резерв есть, "выхлоп" от дальнейших вложений почти равен нулю.
Так вот. Это вся песня про "5 мс" чушь и ерунда. Если вы сами такое говорите — читайте эту статью. А если вам такое говорят — готовьте аргументы. Раньше, когда я слышал эти слова, я злился, но я уже не злюсь. У меня, как у того горшка с петуньей из "Автостопом по галактике" только одна мысль: "Ну вот опять...".
Кто виноват?
Почему БД такая медленная? Ну казалось бы, типичный сервер с 20-64 ядрами на частоте 2-3 ГГц способен выполнить 50-150 миллиардов простых операций, а максимальные (синтетические) тесты баз данных показывают на подобных машинах всего лишь 10000-50000 транзакций в секунду. Эй! Это ж от миллиона до десятка возможных миллионов операций на транзакцию. Это не просто много, это очуметь как много.
Такого оверхеда стоят ACID-требования к транзакциям.
- Atomicity — либо вся транзакция выполнена, либо вся не выполнена.
- Consitancy — на входе и на выходе из транзакции система в целостном состоянии
- Isolation — транзакции не видят промежуточных состояний друг друга
- Durability — если уж транзакцию успешно завершили (закоммитили), то вне зависимости от обстоятельств внесенные изменения должны остаться в системе.
К слову, буква-в-букву эти требования не выполняются почти нигде и никогда, а в распределённых системах просто никогда (CAP-теорема мешает). Для нашей ситуации скорее всего дороже других стоит требование "D", это требование обеспечивается ключевым механизмом всех распространенных OLTP СУБД: WAL, write-ahead log (PostgeSQL), он же журнал транзакций (SQL Server), он же REDO log (Oracle). Вот он — камень на шее производительности, и он же фундамент Durability транзакций.
Что такое WAL
Давайте ненадолго забудем про современные SSD, про крутые СХД. Пусть у нас есть сервер, в нем один или несколько дисков.
Любая транзакция, даже вставка одной записи, как минимум потенциально, а на самом деле почти всегда и реально — неатомарное действие. Нам почти всегда нужно изменить не только ту страницу, где лежит запись, но и страницы индексов, возможно, служебные страницы. При этом в одной транзакции одна и та же страница может поменяться много раз. Плюс у нас параллельно могут выполняться другие транзакции. Более того — соседние во времени транзакции постоянно "теребят" одни и те же страницы. Если мы будем дожидаться записи каждой страницы на диск перед продолжением действий, а именно это по сути требуется в Durability, то нам придётся записывать во много раз больше и ждать обязательного окончания каждой записи на энергонезависимый носитель. Никаких кешей, никакой перестановки операций в очереди, а иначе не будет целостности! Более того, нам как-то надо отмечать какие данные уже по фиксированным транзакциям, а какие — ещё нет (и какие были данные раньше). Для понимания — типичный одиночный жёсткий диск (HDD) в таком режиме даст 50-100 IOPS и это константа уже 20 лет. На одну даже маленькую транзакцию потребуется 5-10 операций записи. Ах, да, чтобы знать, что записывать — надо прочитать. Даже очень-очень высоконагруженные на запись OLTP системы читают в раза 3 больше чем пишут. Таким образом наша транзакция стоит 20-40 IO, а значит 0,2-0,8 секунд на диск.
2 транзакции в секунду. Маловато? Давайте попробуем раскидать по дискам? Ой, а нам надо всё равно дожидаться пока предыдущий запишется и параллельность в итоге отсутствует. Как быть? А давайте заведем файл-журнал в который будем последовательно записывать все операции записи в БД и отметки транзакций! Плюсы:
- Информация об операции может быть гораздо компактнее чем запись всей страницы (типичный размер страницы 8 КиБ, информация записываемая в журнал часто 0,5-1 КиБ).
- Вместо записи о том зафиксирована транзакция или нет прямо в страницу — достаточно меток о начале и фиксации транзакции в журнале.
- Страницы можно записывать не после каждой транзакции — ещё в несколько раз меньше. Процесс чтения/записи данных полностью "отвязан" от журнала.
- Главное. Если наш журнал положить на отдельный диск и писать записи последовательно, то за счет того, что не требуется постоянно перепозиционировать головки диска, даже бытовой HDD в таком режиме выжимает до 1000 IOPS, учитывая что мелкие транзакции "стоят" 2-4 записи в журнале, то можно выжать 200-400 TPS
- При сбое состояние файла данных можно восстановить по такому журналу, а при отмене транзакции по нему можно откатить изменения
Такой журнал и называется write-ahead log/transaction log/REDO log.
Ура! Супер! Было 2 транзакции в секунду, стало 300 — улучшили в 150 раз. А какой ценой? Как выясняется, цена значительна:
- Во всех распространенных СУБД запись в журнал строго последовательна. За запись в журнал отвечает один поток. У вас 100 процессоров? Круто. А в журнал всё равно будет писать один поток. Глубина очереди — ровно единица.
- Всё ещё — никаких кешей ОС, никаких перестановок операций. Требования durability остались. Операции write-through: пока диск не ответил "я записал, я точно-точно записал прямо на поверхность, а не в кеш" СУБД не продолжает работу.
- Если файл журнала положить на диск с данными, то почти все преимущества последовательной записи пропадут. Более того — по хорошему, если несколько баз на сервере, то и несколько дисков под журналы.
- Откат транзакций (по крайней мере в MS SQL Server) — прочитать журнал и восстановить по нему состояние. Это столько же или даже больше операций записи, сколько было операций записи в транзакции. Rollback — дорого!
Это объяснение очень упрощенное, "на пальцах". Для нашей темы этого хватит. WAL — ключевой, фундаментальный механизм обеспечения транзакционности, он обязательно write-through, доступ однопоточный только на последовательную запись, с точки зрения хранилища глубина очереди 1.
Тему write-ahead logging в БД должен хотя бы в минимальном объёме знать каждый, кто так или иначе администрирует СУБД, или инфраструктуру СУБД, или разрабатывает базы данных.
WAL и СХД
Производители СХД "с рождения" сталкиваются с СУБД. Именно для баз данных бизнес покупает эти безумно дорогие комплексы: от street price хранилищ Dell-EMC, HP, Hitachi, NetApp при верстке бюджета глаза наполняются слезами у большинства топ-менеджеров, если они, конечно, не получат процент от этой цены. Но тут есть инженерно-маркетинговый конфликт. Я его поясню на примере Dell-EMC, но только потому что помню, где у них документация.
Итак:
- Журнал однопоточный, без очереди
- Журнал write-through, то есть задержки (latency) "вечные" по сравнению с производительностью CPU
- OLTP-нагрузки это много относительно небольших транзакций,
- Большинство других нагрузок СУБД так или иначе параллелятся.
Закон Амдала беспощадно говорит нам, что однопоточная низкопроизводительная нагрузка сделает бесполезными добавление процессоров, а производительность будет определяться именно журналом. Более того, в этот момент нам станет наплевать на производительность СХД в IOPS, а станет важным только latency.
Но не стоит сбрасывать со счетов другие дисковые операции — чтение и запись в файлы данных и в tempdb
. Чтение — тоже "ждущая" операция. Пока страница данных не прочитана с диска в память, процессор её обрабатывать не может. Но для этих операций возможны большие очереди и перестановка операций в этой очереди: СУБД часто знает, какие страницы надо дозагрузить в память, какие сбросить на диск и ставит в очередь на чтение сразу много. Так как в этом сценарии важно, когда закончится последняя операция из пачки, то в этой нагрузке нам наоборот важнее IOPS, чем latency отдельной операции. Для понимания масштабов: операций чтения в типичной OLTP системе 85%-95%. Да-да-да, операций записи на порядок меньше.
Инженеры-разработчики СХД вендора плотно работают с вендорами СУБД, и отлично понимают все технические нюансы работы СУБД с дисковой подсистемой. Правильное планирование, разбиение и выделение дисковых ресурсов для СУБД — сложная и важная компетенция администратора СХД. У того же Dell-EMC даже базовые white-paper H14621 и H12341 по рекомендациям разбиения для SQL Server — по сотне страниц. Эй! Это не детальная дока, это самый общий white-paper! Там есть ещё куча специфичных (h15142, h16389… там их тьма). Не отстают и "смежники" из VMware — Architecting Microsoft SQL Server on VMware vSphere. Обратите внимание, это документы не только и не столько для DBA, сколько для админов инфраструктуры и СХД.
Ещё отмечу, что во всех этих документах нарезаются отдельные LUN для данных, для журналов и для базы tempdb
. Да, где-то в свежих документах аккуратно говорят, что для All-Flash решений нет смысла отделять журналы на физически отдельные носители, но LUNы все ещё предлагают нарезать отдельно. Если данные и журналы свалить в один LUN, то с точки зрения ОС это будет одна очередь IO. И вот тут будет проблема. У операций с журналом latency станет сразу на порядок больше. А из-за того, что в очереди появятся неперемещаемые операции с журналом, просядет IOPS на файлах данных и tempdb
. Это не "открытие века", это азбучная истина работы с БД. Она не устарела и не отменена с появлением All-Flash. Да, задержки на операции с SSD быстрее на порядок, чем на операции с HDD, но всё ещё на пару порядков медленнее операций с памятью. IO всё ещё узкое место СУБД.
И в технических документах правильно делается акцент, что в журналах транзакций не важно количество IOPS, а важно, чтобы latency была минимальной (в современных пишут, что менее 1 мс).
А маркетологам надо продавать. Гиперконвергентность! Виртуализация! Гибкость развертывания! Дедупликация! Простота настройки! Много-много IOPS! Красивые презентации, уверенный голос, строгие костюмы. Ну а как иначе продать решение с 6-7-значным ценником в долларах? За этим как-то забывается, что от СХД можно добиться либо latency, либо throughput, но не обоих сразу, что какая-нибудь лицензия на балансировщик нагрузки стоит как еще одна полка, что если интенсивная запись будет длиться больше часа, то оперативной памяти контроллеров не хватит и производительность просядет до "как будто кеша нет", что обучение сотрудников заказчика стоит еще 100000 рублей за первый курс, ну и подобные уловки…
5 мс
То ли наслушавшись-начитавшись маркетологов, то ли от лени, то ли ещё из-за каких-то тараканов, но почему-то часто админы СХД делают примерно так. Берем большую полку, всю её объединяем в что-то плоское, нарезаем на thin provisioned LUNs и раздаём по LUN на сервер. Или по два, потому что "системный раздел хорошо дедуплицируется". А когда, я вижу, что с дисковой подсистемой со стороны SQL ад-ад-ад, то начинается та самая песня, что "5 мс отличный показатель", что "100000 IOPS", "Ваша нагрузка на СХД менее 5%"
НЕТ.
- Для OLTP систем на разделе с WAL/журналами транзакций 5 мс это недопустимый показатель. На "почти-коммодити" железяке за цену в 1000 (прописью: в тысячу) раз дешевле нормальным показателем сейчас будет 0,1-0,3 мс. А завтра — 0,01 мс. Скорость, как у HDD 2008 года по цене целого подъезда квартир в Москве не нужна. Никакое "удобство обслуживания" этого не стоит.
- Вендор пишет, что журналы транзакций не требовательны к IOPS и их можно положить на HDD? Да, это так. Но для этого надо чтобы эти диски ни одна
заразазадача кроме записи журналов СУБД не трогала. И чтобы СХД отвечала серверу, что данные записаны, сразу как данные легли в энергонезависимую память (это гораздо раньше, чем они будут записаны) - "Тонкие" диски для реальных OLTP БД — зло.
- Для WAL абсолютно неинтересно сколько там можно выжать IOPS на глубине очереди 10 или 20. Там нет глубины.
- Для WAL абсолютно не показатель, что очередь IO в ОС "всего лишь около 1". Она не будет больше.
- Нет, DBA и разработчики БД не "дятлы криворукие, которые не могут нормально настроить, чтобы запись в WAL параллелилась" (реальное мнение админа)
- Логика любителей считать утилизацию "раз ваша система нами криво настроенная в один раздел не делает 10000 IOPS, значит её надо перенести с high-end массива на mid-range" — это неправильная логика.
- Если у 40-ядерного сервера загрузка процессора 2,5 процента, то это не значит, что ему нечем заняться, а, скорее всего, значит, что есть какая-то задача, которая блокирует все остальные.
Когда какая-нибудь загрузка данных на ноутбуке разработчика выполняется 5 минут, а на 40 ядерном сервере с 1 ТиБ RAM и СХД за полмиллиона долларов та же самая задача выполняется час, то даже у самых терпеливых заказчиков появятся вопросы обоснованности затрат.
Средняя задержка на разделе с WAL | никогда не будет в секунду больше транзакций, чем: |
---|---|
5 мс | 200 |
1 мс | 1000 |
0,5 мс | 2000 |
0,1 мс | 10000 |
0,05 мс | 20000 |
Что делать
Советы администраторам и DBA
Для OLTP перестаньте считать "утилизацию" и IOPS. Отдельно замечу — совсем не смотрите на IOPS с большой глубиной очереди: даже на разделах с данными большие очереди обычно короткий всплеск или что-то, что не влияет на реальную производительность OLTP.
Делить дисковое пространство на LUN — это не прихоть DBA. У базы данных есть несколько разных профилей нагрузки дисковой подсистемы. Как минимум можно выделить следующее:
- Работа с файлами данных. Обычно это чтение и запись случайными блоками по 8/64 КиБ. Чтений 80-95%. Очереди возникают: в периоды обслуживания, в периоды массовой загрузки, на неэффективных или массовых запросах и при checkpoint. На производительность влияет отзывчивость по чтению. Важно, чтобы выравнивание 8/64 КиБ блоков "всквозную" проходило через всю систему хранения.
- Работа с
tempdb
— то же самое, что и работа с файлами данных, но чтений обычно 40-75% и отзывчивость на запись может быть важной. В современных системах MS SQL эта БД может быть загруженной в несколько раз сильнее, чем БД с данными. В некластерной конфигурации СУБД этот раздел должен быть исключен из всяких СХД-шных репликаций. Его содержимое после перезагрузки сервиса всё равно будет уничтожено. - Работа с архивными данными/DWH. Чтений близко к 100%. Размер одного блока чтения обычно 64 КиБ. Запросы читают много и подряд, поэтому очередь может скакать и до 1000 и больше.
- Работа с журналами транзакций. Чтение только для обслуживания (резервное копирование, репликация и т.п.), на производительность приложений влияет только запись. Запись блоками 0,5-64 КиБ. Без очереди, в один поток. Задержка критична для приложений.
- Резервное копирование и восстановление. С точки зрения БД это чтение большими блоками (часто 1 МиБ). Важно, что эта нагрузка может упереться в каналы/шины (и FC, и Ethernet) и в производительность процессоров СХД в некоторых случаях. Резервное копирование одного сервера может повлиять на производительность других серверов того же SAN/СХД.
- Работа с файлам приложения: это логи, default trace, бинарные файлы и т.п. Эта нагрузка редко бывает существенной и важна только при старте системы.
Есть и другие виды нагрузки, но они слегка экзотичны (например, может быть хранилище файлов, хранящихся в БД в виде каталога FileStream). У всех этих видов нагрузки разные, зачастую противоречащие требования к дискам. Если они все свалены на один раздел, то вы не только ухудшаете производительность, но очень важно, что лишаетесь возможности понять из-за чего система тормозит, а также лишаетесь возможности улучшить только ту часть которая требует улучшения без глобальных улучшений/апгрейдов СХД. Поэтому главная рекомендация:
Ознакомьтесь с рекомендациями производителя СХД и СУБД, постарайтесь разобраться "почему они так советуют" и спроектируйте систему с учетом разных видов нагрузки. Разнесите принципиально разные виды нагрузки на разные разделы.
Ну и до кучи
- Читайте не маркетинговый бред, а техническую документацию. Для Dell/EMC и SQL Server начните со ссылок в статье.
- Техническую документацию проверяйте замерами. Эти замеры сравнивать с неким "коммодити" примером (например, на NUCах c SSD, как вариант, да). Работайте по циклу гипотеза-проверка-анализ, честно проверяя свои гипотезы.
- Администраторам СХД и ферм виртуализации нужно планировать развертывание вместе с DBA, если там будут сколько-то нагруженные БД (даже 200 транзакций в секунду).
- Если у вас используется геораспределённная синхронная кластеризация (МetroСlaster и его родня), посмотрите, не вносит ли он задержек, которые недопустимы. Эти кластеризации могут давать запросто +0,5 мс, и когда было 0,2, а с кластером стало 0,7 то производительность упадет до 3 раз.
- Проектировать надо понимая, что со временем ситуация изменится и что систему потребуется изменять. Если у вас сейчас раздел
tempdb
не нагружен, то, возможно, разработчики БД в следующей версии включат RCSI и в 12 чаов ваша карета превратится в тыкву. - Latency почти всегда важнее throughput. И это продолжает быть верным и в облачных технологиях, и в "гиперконвергентных системах", и виртуальных фермах. Если вы улучшаете throughput увеличивая latency, то скорее всего ошибаетесь. Такое делать можно только очень обоснованно.
MS SQL Server
Если говорить про MS SQL, то на самом деле есть некоторое количество способов снизить нагрузку на bottleneck журнала транзакций, может кому-то поможет:
- Часто я вижу рекомендацию не делать большие транзакции. Это правильно. Но транзакции не должны быть и слишком мелкими. 1000 транзакций подряд вставляющие по одной строке могут быть в 5-30 раз дольше одной транзакции с 1000
INSERT
. И, да, напомню, что если вы не открыли транзакцию явно, то поведением по умолчанию будет "каждая команда — отдельная транзакция". - У
tempdb
журнал транзакций "не настоящий". У него есть кеширование. Поэтому, если вам нужны промежуточные вычисления, то не делайте их в постоянных таблицах. - Если нужно вставить много записей, то следует использовать BULK INSERT или другой вариант минимально протоколируемых операций. Минимально протоколируемыми могут быть только операции типа вставки записей и перестроения индекса, и только в моделях восстановления "Simple" и "Bulk logged". И, кстати, во всех остальных случаях разницы производительности между моделями Simple/Bulk logged и Full нет никакой. По массовой загрузке до сих пор самое полезное — The Data Loading Performance Guide, но эта статья постепенно устаревает. На закуску (правда про ETL, а не OLTP) ещё одна статья почти десятилетней давности We Loaded 1TB in 30 Minutes with SSIS, and So Can You
- В свежих версиях SQL Server есть Delayed Transaction Durability — режим, в котором можно получить производительность ценой надёжности.
- В свежих версиях SQL Server есть In-Memory OLTP. Технология с кучей ограничений, но при грамотном точечном применении может оказаться полезной.
- Посмотрите, нет ли ненужных синхронных зеркалирований, ненужных синхронных AlwaysOn реплик.
***
Вот и всё. Нет никой магии. 20000 IOPS с 5 мс latency и с очередью 4-16 ничего не говорит о производительности СХД для задач OLTP. Для OLTP надо правильно размечать СХД, правильно выбирать метрику производительности и уметь измерять её.
На горизонте есть очень интересный новый участник борьбы за производительность систем хранения для БД. Это Intel Optane. У нынешних SSD "красивые" цифры производительности начинаются от глубины очереди 4, что на самом деле не очень жизненно. Плюс эта производительность на запись обеспечивается некоторым объёмом оперативной памяти внутри SSD, и, если запись идёт достаточно интенсивно, то потом наступает существенная деградация производительности. А еще у SSD ограничен ресурс. Да, у современных серверных и топовых "гражданских" он достаточно большой, но совсем не бесконечный. И тут на сцене появляется Intel Optane: судя по тестам несерверных моделей (раз, два ) задержки на случайных операциях на очереди глубины 1 выходят на уровень около 20 микросекунд. Это по сути без кеширования, без деградации. SSD при таком же профиле начинают тупить до 100-300 мкс. Ресурс уже сейчас выходит за типичный ресурс SSD.
Ну цена, да. Но потенциально эта железка открывает новые горизонты производительности OLTP "традиционной", не in-memory архитектуры без отказа от ACID. А с другой стороны latency 20 мкс заставляет задуматься о судьбе "обычных" СХД. На low-latency требованиях им будет очень тяжело конкурировать с Optane (снова привет встроенным системам хранения?).
Это всё очень круто и я надеюсь на успех (и подешевление) Optane.
Комментариев нет:
Отправить комментарий