Немного об анализе производительности диска

Как измерять производительность и как интерпретировать цифры

Очень часто при диагностике проблем ставится “диагноз” “диск медленно работает” и в доказательство приводится статистика disk utilization от утилиты iostat или load average от утилиты top. Насколько можно доверять этим цифрам?

И при тестировании дисков, и при диагностике проблем, самая частая ошибка которую совершают многие - посмотреть на одну отдельно взятую характеристику и на основании неё принимать решение.Вторая по частоте ошибка - принимать решение на основании результатов чисто “синтетических” тестов.

Попробуем немного упорядочить процесс тестирования и рассказать почему просто “запустить fio 4K случайной записью” или “посмотреть утилизацию в iostat” и на основании этого принять решение - не самая лучшая идея.

Важные характеристики

  • Время обслуживания (Service time)
  • Количество обслуживаемых одновременно запросов (количество потоков, IO depth)
  • Количество операций в секунду (IOPS)
  • Полоса пропускания (мегабайт (гигабайт) в секунду)

Во всех тестах смотрят на последние две характеристики (мегабайты в секунду и количество  операций в секунду) и изредка на первую - но на самом деле, нужно смотреть на первую и вторую, поскольку третья - количество операций в секунду - вычисляется как 1000 / service_time_ms * iodepth, а четвертая - мегабайты в секунду - просто третья (IOPS) умноженная на размер блока. И уж точно бессмысленны“комбинированные тесты чтения- записи” поскольку они практически всегда выглядят как “читаем много раз / пишем много раз” и в результате их результаты тривиально получается перемножением результатов тестов чтения и записи на соответствующие коэффициенты.

Роль параллельности в производительности

Современные диски - и особенно виртуальные диски, которые мы видим в облаках и на хостингах, являются многопоточными. То есть, они способны обрабатывать насколько запросов одновременно. Поэтомунаиболее важными (и основными) характеристиками являются не привычные нам “мегабайты в секунду” и не “операций в секунду”, а именно время обслуживания и количество потоков. А “мегабайты всекунду” и “операции в

секунду” являются всего лишь производными от базовых характеристик. В большинстве случаев, при увеличении количества потоков сначала растет количество операций в секунду с сохранением времени обслуживания, а затем начинает расти время обслуживания, а количество операций в секунду становится примерно постоянным.

Поэтому, когда мы начинаем тестирование диска, мы сначала смотрим именно время обслуживания и количество потоков - и только потом делаем выводы. Для того, чтобы понять возможности нашего диска, мы будем запускать один и тот же тест последовательно несколько раз, на каждом шаге увеличивая количество потоков с помощью параметра iodepth. Результаты фиксируем в таблице. Количество операций в секунду не то чтобы интересно - оно однозначно определяется по формуле 1000 / время обслуживания(ms) * количество_потоков.

Начинаем - первый тест запускаем в виртуальной машине в VMWare Player:

# cat generic.fio [task0] ioengine=libaio blocksize=4096 rw=randwrite direct=1 filename=/dev/sdb

И сам запуск тестов:

  • fio generic.fio --iodepth=1
  • fio generic.fio --iodepth=2
  • fio generic.fio --iodepth=3
  • fio generic.fio --iodepth=4
  • fio generic.fio --iodepth=5
  • fio generic.fio --iodepth=6

Запишем данные в таблицу и построим график - тогда становится очень хорошо заметно, что с ростом числа потоков свыше 2 прироста производительности не наблюдается- запросы фактически ставятся в очередь, а значит растет время ожидания (время обслуживания):

IO depth

Latency

IOPS

Utilization %

1

0,636

1566

92

2

0,832

2397

98

3

1,217

2460

99

4

1,621

2463

100

5

2,018

2474

100

62,436

2460

100

Интерпретируем данные: данный виртуальный диск может параллельно обрабатывать 2 запроса на запись - начиная с 2 потоков у нас растет только время обслуживания, среднее время обслуживания в каждом потоке порядка 0,5ms. Обратите внимание на утилизацию диска (ее можно увидеть и в выводе fio, и в выводе iostat) - казалось бы, в однопоточном тесте эта характеристика показывает нам 90% загрузку и если бы мы не довели тесты до конца, то могли бы предположить, что этот диск выдаст максимум 1800 IOPS, что даже близко не верно.

Что это означает для нас?

Это означает, что нельзя слепо верить iostat. Если iostat сообщает, что использование дисков 100% - это не обязательно значит, что нам нужен новый диск, возможно, что мы просто “вычерпали” производительность одного потока.

Почему этот тест был запущен в vmware, а не в облаке MCS?

Очень просто - в продакшен мы выкатываем высокопроизводительные решения, которые очень хорошо "параллелятся", поэтому увидеть эти эффекты в бою будет затруднительно.

Как это относится к “облачным” дискам?

Учитывать параллельность особенно важно в виртуальном окружении - ведь практически всегда диск вашей виртуальной машины лежит на системе хранения, которая очень хорошо “параллелится” и способна одновременно обрабатывать очень большое количество запросов. Виртуальные диски, которые предоставляются вашим виртуальным машинам, в большинстве случаев хорошо масштабируются “горизонтально”, но обладают несколько большим временем обслуживания, чем локальные диски. Это цена отказоустойчивости.

Облачные диски против софт-рейда

Софт-рейд в Linux фактически однопоточен. Каждую операцию записи софт-рейд сопровождает изменением суперблока, поэтому софт-рейды в линуксе практически всегда работают а) в один поток и б) со скоростью половины скорости одного потока. 

А это значит, что использование софт-рейд на SSD фактически приведет к потере 90% производительности (если не прибегать к различным ухищрениям, конечно).

Что это значит для нас? 

Для нас это означает, что RAID’ы внутри виртуальных сред в общем-то бесполезны - разворачивая софт-рейд внутри виртуальной машины, мы удваиваем время обслуживания и одновременно теряем возможности параллельного выполнения запросов, которые нам дают СХД инфраструктуры виртуализации.

Что такое “IOPS per GB”

Создавая большое хранилище для дисков виртуальных машин, провайдеры облачных услуг используют много жестких дисков и SSD. В большинстве случаев, диски виртуальных  машин равномерно “размазаны” по всем дискам хранилища. Это значит, что когда виртуальная машина читает или пишет данные, она работает примерно равномерно со всеми дисками хранилища, и теоретически может утилизировать всю производительность хранилища, оставив остальных клиентов без ресурсов.

Чтобы такого не происходило, облачные провайдеры ограничивают производительность по объему - чем больший объем клиент заказал, чем больше IOPS он получит. Это ограничение жесткое и, как правило, накладывается на каждый виртуальный диск. При этом задается также нижняя планка (она задается такой, чтобы не получить “бесполезный” диск- например, чтобы не получить виртуальный диск на который разрешено 10 IOPS) и может задаваться верхняя. При этом каждый диск в виртуальной машине в результате может иметь свои собственные ограничения.

Как это влияет на производительность?

До тех пор, пока вы не достигли лимита, время обслуживания является “настоящим”. Как только вы достигли лимита, ваши операции ставятся в очередь. Это называется throttling и происходит вне зависимости от количества потоков. Превысили лимит - нас начали троттлить. Нас начали троттлить - начало расти среднее время обслуживания.

Ситуация, когда вы начинаете упираться в производительность диска, на самом деле совсем не обязательно значит, что диск работает “плохо” - вполне возможно, что диск работает ровно на столько, сколько ресурсов выделено вашей виртуальной машине.

Как узнать истинное время обслуживания вашего виртуального диска с учетом того, что могут стоять ограничения на количество операций?

Сделать это можно в fio указанием параметра rate_iops. Этот параметр лимитирует количество операций в секунду отправляемых fio в ходе теста, так что вы получаете картину, не искаженную тротлингом:

fio —rw=randread —iodepth=64 —runtime=10

Без ограничения: IOPS = 10000, latency=6,328ms

С ограничнием rate_iops=10000: IOPS=10000, latency=0,623ms

Таким образом, если мы забудем про ограничения, наложенные уровнем сервиса, то получим очень сильно отличающиеся от жизни результаты.

Как производительность диска влияет на Load Average

Нередко приходится сталкиваться с ситуацией, когда администратор виртуализированной системы начинает утверждать, что диск стал хуже работать, а в качестве доказательства приводит процент утилизации диска (“вот видишь, у меня диск на 100% занят” - но мы это уже видели выше и знаем, почему верить этому нельзя) и load average (“вот видишь, у меня LA выросло, значит процессор ждет диска!”).

На самом деле, и ограничения по количеству потоков на диске, и квоты на операции в секунду в некоторых случаях приводят к одному и тому же - ваши операции приостанавливаются и начинает расти время обслуживания - как мы его видим из гостевой операционной системы. И в некоторых случаях (только в некоторых!) Load average   начинает увеличиваться. Но в действительности значение loadaverage намного больше зависит от того, какие программы работают с вашим диском, и в каком режиме   (асинхронном или синхронном), чем от возможностей дисков. 

В следующем примере мы запускаем 2 серии тестов на облачный диск, но одна серия тестов использует асинхронный ввод-вывод (когда процесс отправляет несколько операций и потом дожидается их завершения), а вторая - синхронный, когда у нас есть несколько процессов, каждый из которых отправляет одну запись и дожидается её завершения:

IO

threads /jobs

 

Read time AIO(ms)

 

Read OPS AIO

Read Time PSYNC

(ms)

 

Read OPS PSYNC

 

Write time AIO(ms)

 

Write Ops AIO

Write time PSYNC

(ms)

 

Write OPS PSYNC

 

Disk

utilization

 

AIO load average

 

PSYNC

load average

 

1

0,908

1093

0,722

1373

1,981

503

1,879

529

91

0,07

0,875

 

2

0,837

2374

0,709

2794

1,842

1082

1,808

1101

93

0,19

1,76

 

3

0,844

3530

0,692

4297

1,701

1757

1,702

1755

98

0,295

2,635

 

4

0,776

5121

0,681

5824

1,665

2393

1,668

2388

100

0,49

3,545

 

5

0,763

6504

0,663

7473

1,648

3023

1,604

3104

100

0,66

4,44

 

6

0,740

8048

0,657

9054

1,670

3580

1,619

3691

100

0,765

5,325

 

7

0,744

9350

0,671

10300

1,623

4297

1,601

4355

100

0,82

6,205

 

8

0,718

11100

0,631

12600

1,556

5152

1,606

4962

100

1,065

7,065

 

9

0,725

12300

0,619

14400

1,691

5304

1,651

5429

100

1,17

7,945

 

10

0,716

13900

0,639

15500

1,543

6458

1,626

6119

100

1,23

8,815

 

11

1,095

9990

0,749

14500

1,694

6471

1,629

6723

100

1,32

9,69

 

12

0,864

13800

0,734

16200

1,755

6814

1,787

6688

100

1,52

10,585

13

1,003

12900
0,779
16600

1,685

7687
1,770

7315

100

1,63

11,46

14

1,195

11700
0,868
16000

1,733

8054
1,633
8542100

1,76

12,37

150,879160000,777
19200

1,804

82881,786

8367

100

1,90

12,805

16

1,04215300
0,875
181001,636
97461,626

9803

1001,95

14,11

Тесты проводятся на одной и той же виртуальной машине, на одном и том же виртуальном диске, и мы видим очень интересную картину - несмотря на то, что load average у нас выше, диск на самом деле работает нормально.

А еще эта таблица показывает, что считать время надо раздельно для чтения и для записи.   

Если же мы опустимся “внутрь” гостевой операционной системы и вспомним, что LA это количество потоков, которые не просто ждут чего-то - но ждут кванта времени на выполнение, то получаем - высокие значения Load Average означают не то, что диск работает медленно, а то, что диск работает достаточно быстро, но процессора уже не хватает, и некоторые потоки которые разблокированы и готовы начать обработку данных, простаивают в ожидании процессора.

То есть Load Average не является показателем нагрузки системы, а всего лишь свидетельствует, что у вас имеет место одно из следующих условий (или комбинация условий):

• В вашей системе работает много приложений с синхронным блокирующим I/O и не хватает процессора, поскольку I/O достаточно быстр, чтобы система “затыкалась” на процессоре.

• В вашей системе работает много потоков, которые интенсивно используют процессор.

Но ни та, ни другая ситуация не означает, что у вас проблема с диском. То есть высокий LA - это нехватка процессора, а не нехватка ввода-вывода.

Как обманывает iostat и почему мы его все равно любим

  • 1.     Определяем как внешне проявляется проблема
  • 2.     Убираем из диагностических данных неверно интерпретированные данные
  • 3.     Корректируем имеющиеся данные чтобы они отражали реальную ситуацию
  • 4.     Собственно понимаем, есть проблема или нет (например клиент неправильно интерпретировал данные и предположил что есть проблема в работе сервиса)
  • 5.     Решаем проблему если она есть

Когда мы говорим о виртуальных дисках, то первым инструментом диагностики служит iostat. Он активно обманывает - но тем не менее очень полезен. Давайте посмотрим вывод

iostat -xNky 

(x - расширенная статистика, k - в килобайтах, y - пропустить первый “сводный” отчет чтобы отображать текущую картину а не историческую).

                                                                        

А вот реальный вывод fio (оставлено только самое важное):

                       

generic: bs=(R) 13.0KiB-13.0KiB, (W) 13.0KiB-13.0KiB, (T) ioengine=libaio, iodepth=8 ... generic: (groupid=0, jobs=1): err= 0: pid=3639: Thu Mar 28 15:33:17 2019                       

   read: IOPS=2950, BW=37.5MiB/s (39.3MB/s)(6651MiB/177587msec)    lat (usec): min=495, max=89073, avg=1330.13, stdev=1010.89                       

... write: IOPS=1591, BW=20.2MiB/s (21.2MB/s)(3589MiB/177587msec)

    lat (usec): min=1209, max=153659, avg=2538.78, stdev=1973.23                  

Что у нас коррелирует:

  • Количество IOPS по чтению и записи 
  • Глубина очереди
  • Размер блока

Что не коррелирует или бессмысленно: 

  • Процент утилизации диска
  • Время обслуживания и latency
                       

Время обслуживания (service time) который нам показывает iostat - на самом деле является средним временем обслуживания, поделенным на глубину очереди. А утилизация диска не говорит только то, что диск постоянно занят запросами. Но это не означает, что он не способен выдавать больше операций - если добавим потоков (увеличим глубину очереди) - то начнет, ведь за этим виртуальным диском стоят десятки SSD.

То есть, iostat - это очень полезная утилита для определения профиля нагрузки, но не для определения загруженности диска и не для определения времени обслуживания.

Как определить пригодность диска облачного провайдера для вашего сервиса

  • во сколько потоков пишет и читает ваше приложение (приложения)
  • насколько активно оно пишет (количество операций чтения и записи)
  • каким размером блока осуществляется чтение/запись

Можно использовать iostat для приблизительной оценки, можно использовать atop.

На основании указанных данных сформируйте задание для fio. При создании задания самым важным будет правильно указать размер блока и правильно указать распределение чтения/записи. Очень часто оказывается выгоднее в файле конфигурации теста для fio определить два задания - одно на чтение, другое на запись, и в каждом из них указать rate_iops.

И также следует оценить - можете ли вы увеличить степень параллельности вашей нагрузки (заставить приложения отправлять больше запросов одновременно).

Заключение

  1.  Уточните ваш тарифный план и соглашение о сервисе.
  2. Протестируйте диск в один поток с помощью fio, не забывая про rate_iops. Если  забудете - получите неадекватные значения, и можете принять неправильное решение - особенно если вам нужны диски с малой задержкой под малопоточную нагрузку (например, для журналов БД).
  3. Протестируйте диск в несколько потоков, фиксируя результаты тестов - например, в 1, 4, 8, 16, 32 и 64. Результаты с большими значения потоков тоже полезны, особенно для профилей “мало записей - много чтения”.
  4. Оцените, сможете ли вы эффективно использовать возможности параллельного ввода- вывода (не обязательно асинхронного). Если сможете распараллеливать, то вы сможете использовать диски СХД инфраструктуры виртуализации и о сохранности данных будет заботиться СХД.
  5. Тестируйте раздельно чтение и запись. Комплексный режим “70% чтения + 30% записи” в действительности не интересен - его результаты тривиально предсказываются на основании раздельных тестов.
  6. Собирайте статистику iostat -x или atop - она пригодится для диагностики и потребляет не так уж много места.
  7. Load Average свидетельствует скорее о нехватке процессорного ресурса и/или наличии большого количества потоков с блокирующим I/O, но не о недостаточной производительности диска.
  8. Практически всегда время обслуживания на дисках расположенных на СХД имеют latency 0.5ms и более. Диски с низким очень низким временем отклика как правило локальные и не резервированные - вам придется предусматривать резервирование на уровне сервиса.
  9. Софт-рейд (mdadm, dmraid) во всех структурах плохо скалируется на количество потоков.