В последнее время в облачных средах и хостингах все чаще стали попадаться «виртуальные» жесткие диски. Техническая служба хостера может заверять, что «виртуальный» диск — быстрый, как десяток рейдов 10 (рейд 100 ;-) ) и держит сотни, а то и тысячи IOPS – однако MySQL заметно для клиентов тормозит. А как это доказать хостеру?
Проблема в том, что измерить «скорость» виртуального жесткого диска изнутри виртуальной машины – непросто, т.к. неясно, что мерить в первую очередь, чем и зачем ;-) А сделать это нужно, чтобы убедить администраторов виртуальной конфигурации, что дело не в приложении и настройках MySQL. И нужно было, как говориться, просто «помыть руки» перед чтением мануала к хранилищу.
В статье я проиллюстрирую простую методику нахождения «точки опрокидывания» производительности виртуального жесткого диска, с использованием доступных в дистрибутивах инструментов – sysbench и iostat. Также мы измерим «точку опрокидывания» известных своей тормознутостью виртуальных дисков EBS от Амазона – как обычных EBS, так и Provisioned IOPS EBS (1000 и 2000 IOPS).
Теория
К черту! Слишком много измерений и букв – последовательное чтение/запись, произвольное чтение/запись, перезаписи, влияние файлового кэша ядра, оптимизация очереди запросов, варианты опций и архитектуры файловой системы… Мы поступим проще – сэмулируем многопоточную нагрузку на виртуальный диск, подобную создаваемой сервером MySQL и посмотрим, как диску, или что за ним в сети скрывается, дышится.
А чтобы не было скучно, добавим к gnu.org немного романтики :-)
Как MySQL нагружает диск
Для InnoDB, для типичного веб-приложения, если набор данных не помещается в оперативную память (именно поэтому были изобретены базы данных ;-) ), информация будет, в основном, считываться с диска в произвольном порядке (страницы buffer pool и кластеризованные индексы, хранящиеся на диске), а записываться — последовательно (журналы транзакций и бинарный лог).
Периодически buffer pool из оперативной памяти будет сбрасываться на диск – произвольная запись. Сразу исключаем отсутствие кэша записи и «батарейки» в виртуальном хранилище – оно должно быть, иначе MySQL в режиме ACID (innodb-flush-log-at-trx-commit=1) просто умрет от сожаления.
Важно понять тут, что запросы клиентов MySQL выполняет в параллельных потоках – и диск будет нагружаться, соответственно, несколькими потоками одновременно.
Создаем нагрузку
Начнем с простого EBS-диска амазона. Нагружать виртуальный диск будем инструментом sysbench (в CentOS он доступен пакетах, собрать из исходников несложно):
yum install sysbench
mkdir -p /mount/disk_ebs/mysql/test_folder
cd /mount/disk_ebs/mysql/test_folder
sysbench --test=fileio --file-total-size=16G prepare
Важный момент – создаем суммарный объем тестовых файлов (16G), превышающий как минимум в 2 раза объем оперативной памяти виртуальной машины (не спрашивайте, почему в 2 раза ;-), чем больше — тем лучше). Это нужно для уменьшения влияния файлового кэша операционной системы – при повторном запуске тестовые файлы лучше, поэтому, перегенерить заново (либо создать несколько тестовых папок и переключаться между ними).
Теперь создаем нагрузку в N потоков, эмулируя ~N клиентов сервера СУБД, выполняющих запросы (да я знаю, что внутри СУБД есть несколько служебных потоков, но не будем пока усложнять). Допустим, ожидается, что с БД будут одновременно работать 10 апачей:
sysbench --num-threads=10 --test=fileio --file-test-mode=rndrw --max-time=180 --file-rw-ratio='2' --file-total-size=16G --max-requests=1000000 run
Что измеряем?
Теперь самое интересное. Нам не интересно, какие результаты покажет sysbench – он нам только нагрузку создает. Мы лишь смотрим, как чувствует себя виртуальный диск под нагрузкой:
iostat –xm 10
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await svctm %util
xvdm 0.00 0.00 120.50 0.00 2.05 0.00 34.79 10.50 87.47 8.30 100.00
Диск «завален» запросами большую часть процессорного времени, что видно по «%util = 100» — драйвер диска накапливает запросы в очередь и по мере готовности устройства «скармливает» их (некоторые путают этот показатель с пропускной способностью шины к диску, что, конечно, неверно). Тут ясно, если драйвер вынужден ждать почти все время, то диск – загружен под завязку.
Среднее время обработки одного запроса «svctm» — 8.3 ms. Многовато, но нормально для дисков Амазона. Тут нет ничего криминального – обычная физика.
Среднее время ожидания обработки одного запроса «await» — 87.47 ms и средняя длина очереди запросов в драйвере диска «avgqu-sz» – 10.5. Это много, ждать почти 100 ms для обработки одного запроса! Очевидно, как получается эта величина – грубо размер очереди (avgqu-sz) умножается на время обработки одного запроса (svctm).
Итак, мы видим, что всего 10 конкурентных запросов на произвольное чтение/запись (ключи --file-test-mode=rndrw и --file-rw-ratio='2') приводят замедлению работы с виртуальным жестким диском.
Да так, что приходится ждать один запрос почти 100 ms. А если веб-страница создает 200 запросов к диску – сколько времени она будет строиться? 20 секунд?
Интересно, а при каком числе потоков диск Амазона не начинает накапливать очередь и обслуживает запросы быстрее хотя бы 50 ms (лучше вообще меньше 20 ms — субъективно)? Видим, что при 5 потоках. Конечно, это слабый показатель, без софтварного рейда не обойтись …
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await svctm %util
xvdm 0.00 0.00 127.50 0.00 2.05 0.00 32.88 5.10 39.78 7.84 100.00
Видим, что размер очереди – 5.1 и время выполнения одного запроса – 39.78.
Тестируем «быстрые» виртуальные диски Амазона
Относительно недавно Амазон анонсировал «быстрые» диски с гарантированным числом IOPS (теория IOPS – обширна, как наша страна, погуглите, en.wikipedia.org/wiki/IOPS). Мы знаем, что простые смертные SATA-диски не держат более 100 IOPS (одновременное чтение и запись), а, к сожалению, также смертные 15k SAS-диски – не более ~200 IOPS. Также известно, что SSD-диски и SAN на других технологиях могут осилить сотни, и даже тысячи IOPS – понятно, что они значительно дороже.
Итак, посмотрим, на каком числе одновременных потоков «быстрые» виртуальные диски Амазона начинают тупить и собирать запросы в очередь. Сломаем козе левую ногу!
Диск EBS с 1000 IOPS
Один поток:
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await svctm %util
xvdk 0.00 0.00 1084.50 0.00 27.33 0.00 51.61 3.72 3.43 0.91 99.20
Обратите внимание на маленькое время обработки запроса самим виртуальным диском – 0.91 ms. Видимо массив на SSD ;-) Размер очереди ~4, а среднее время одного запроса – 3.43 ms.
20 потоков:
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await svctm %util
xvdk 0.00 0.00 1059.50 0.00 26.37 0.00 50.98 55.39 51.97 0.94 100.00
Видим, что при 20 потоках запрос придется ждать ~50 ms из-за образования очереди в 55 запросов.
Диск EBS с 2000 IOPS
20 потоков:
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await svctm %util
xvdl 0.00 0.00 1542.50 0.00 36.29 0.00 48.18 33.20 21.29 0.65 100.00
50 потоков:
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await svctm %util
xvdl 0.00 0.00 1498.50 0.00 36.63 0.00 50.06 86.17 57.05 0.67 100.00
Итоги измерений
Видим, что EBS-диск с 2000 IOPS показывает примерно такую же latency (~50ms) на 50 потоках, как диск с 1000 IOPS на 20 потоках и обычный EBS диск на 6-7 потоках (видимо у обычных EBS дисков IOPS находится в пределах 200-300).
Что еще случается
Виртуальные жесткие диски нередко дарят сюрпризы. Иногда их просто недонастроили, т.к. не успели дочитать man…
Недавно столкнулся с подобным кейсом, когда при создании многопоточной тестовой нагрузки на пустом MySQL сервере у «большого-дорогого-быстрого-сетевого» виртуального диска скакал показатель svctm от 0.5 до 1 ms в ночное время и от ~10 до 100 ms – в дневное (хотел померить в полнолуние, не дождался). MySQL разумеется – тормозил. Причина была в параллельно использующих сетевое хранилище и незнающих друг о друге проектах, а не настройках MySQL, который пытались сделать виноватым ;-)
Резюме
Используя подручные инструменты, мы довольно быстро определили предел конкурентной многопоточной нагрузки, при котором виртуальный диск начинает накапливать очередь и обслуживать довольно типичные для MySQL запросы за 50 ms и более. Теперь можно предположить, сколько дисков собрать в рейд, чтобы обеспечить latency, допустим, в 10-20 ms при заданном числе клиентов. Понятно, что это приблизительные данные, но они, безусловно, помогут двинуться дальше, особенно если измерить производительность настоящего жесткого диска/рейда и прийти с этими сравнительными данными и бутылкой шампанского к облачному хостеру;-)
В заключение, поздравляю всех с прошедшим праздником, желаю быстрых виртуальных дисков, надежных серверов и точных измерений! Заходите к нам на Битрикс24. Удачи!
Автор: AlexSerbul