Статьи

Не верь вмстать номера IOwait

 Сегодня я проводил тест на своем старом тестовом компьютере с обычными жесткими дисками (без рейда с BBU) и заметил нечто необычное в статистике использования ЦП.

Тест был выполнен так:

sysbench --num-threads=64 --max-requests=0 --max-time=600000 --report-interval=10 --test=oltp --db-driver=mysql --oltp-dist-type=special  --oltp-table-size=1000000   --mysql-user=root --mysql-password=password  run

А это значит: создать 64 потока и забить базу данных запросами как можно быстрее. Поскольку тест выполнялся на локальном хосте, я ожидал, что эталонный тест полностью насытит систему — будь то использование ЦП или блокировка ввода-вывода этого эталона, поэтому он не тратит много времени на блокировки базы данных, особенно потому, что в этой системе всего 2 сердечники.

Глядя на VMSTAT, я заметил это:

[root@smt1 mysql]# vmstat 10
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
13  0      0 880156  40060 2140824    0    0    12   750  895 1045 32  8 54  6  0
 0  0      0 877116  40060 2141312    0    0     0  1783 2185 23112 44 10 41  5  0
15  0      0 872648  40068 2141960    0    0     0  1747 2204 25743 41 11 46  2  0
 0  0      0 868056  40068 2142604    0    0     0  1803 2164 26224 40 11 44  5  0
17  1      0 863216  40068 2143160    0    0     0  1875 1948 23020 36  9 50  5  0
 0  0      0 858384  40168 2143656    0    0     0  1063 1855 21116 32  9 45 14  0
23  0      0 855848  40176 2144232    0    0     0  1755 2036 23181 36 10 48  6  0
49  0      0 851248  40184 2144648    0    0     0  1679 2313 22832 45 10 40  5  0
10  0      0 846292  40192 2145248    0    0     0  1911 1980 23185 36  9 50  4  0
 0  0      0 844260  40196 2145868    0    0     0  1757 2152 26387 39 11 45  5  0
 0  3      0 839296  40196 2146520    0    0     0  1439 2104 25096 38 10 50  1  0

Рассматривая последние несколько столбцов, мы имеем ожидание ввода-вывода, как правило, в процентах от одной цифры, когда «простаивающий» процессор находится в диапазоне 40-50%. Вы также можете увидеть это визуально на графике из Percona Cloud Tools.
Использование процессора во время теста

Таким образом, система должна быть полностью насыщена нагрузкой, но вместо этого у нас 50% простоя процессора, а iowait обычно в однозначных числах?

Запуск SHOW PROCESSLIST может дать нам хорошую идею в этом случае:

mysql> show processlist;
+-----+---------------+-----------------+--------+---------+------+----------------+--------------------------------------------------------------------+-----------+---------------+-----------+
| Id  | User          | Host            | db     | Command | Time | State          | Info                                                               | Rows_sent | Rows_examined | Rows_read |
+-----+---------------+-----------------+--------+---------+------+----------------+--------------------------------------------------------------------+-----------+---------------+-----------+
|   1 | root          | localhost:39624 | NULL   | Sleep   |    0 |                | NULL                                                               |         0 |             0 |         0 |
| ...
|  32 | root          | localhost       | sbtest | Execute |    0 | NULL           | COMMIT                                                             |         0 |             0 |         0 |
|  33 | root          | localhost       | sbtest | Execute |    0 | NULL           | COMMIT                                                             |         0 |             0 |         0 |
|  34 | root          | localhost       | sbtest | Execute |    0 | Sorting result | SELECT c from sbtest where id between 365260 and 365359 order by c |         0 |             0 |         0 |
|  35 | root          | localhost       | sbtest | Execute |    0 | NULL           | COMMIT                                                             |         0 |             0 |         0 |
|  36 | root          | localhost       | sbtest | Execute |    0 | NULL           | COMMIT                                                             |         0 |             0 |         0 |
|  37 | root          | localhost       | sbtest | Execute |    0 | NULL           | COMMIT                                                             |         0 |             0 |         0 |
|  38 | root          | localhost       | sbtest | Execute |    0 | Writing to net | DELETE from sbtest where id=496460                                 |         0 |             1 |         1 |
|  39 | root          | localhost       | sbtest | Execute |    0 | NULL           | COMMIT                                                             |         0 |             0 |         0 |
...
|  89 | root          | localhost       | sbtest | Execute |    0 | NULL           | COMMIT                                                             |         0 |             0 |         0 |
|  90 | root          | localhost       | sbtest | Execute |    0 | NULL           | COMMIT                                                             |         0 |             0 |         0 |
|  91 | root          | localhost       | sbtest | Execute |    0 | NULL           | COMMIT                                                             |         0 |             0 |         0 |
| 268 | root          | localhost       | NULL   | Query   |    0 | NULL           | show processlist                                                   |         0 |             0 |         0 |
+-----+---------------+-----------------+--------+---------+------+----------------+--------------------------------------------------------------------+-----------+---------------+-----------+
68 rows in set (0.00 sec)

Так что большинство запросов останавливаются на этапе COMMIT. Проверяя настройки мы можем увидеть:

mysql> select @@innodb_flush_log_at_trx_commit;
+----------------------------------+
| @@innodb_flush_log_at_trx_commit |
+----------------------------------+
|                                1 |
+----------------------------------+
1 row in set (0.00 sec)

Это означает, что Percona Server работает в полностью ACID-режиме, что при использовании медленных жестких дисков без BBU означает, что commit вызывает синхронизацию данных на диске, что очень дорого.

Итак, мы выяснили, что на самом деле происходит … но если мы действительно много ждем на диске, почему iowait этого не показывает?
Чтобы ответить на этот вопрос, давайте посмотрим, как на самом деле вычисляется IOwait.

IOwait — это тип простоя (а в более ранних версиях Linux он был включен в простое) — когда у нас есть процесс / поток, который будет запущен, если он не будет ожидать ввода-вывода, мы рассматриваем этот iowait, если он не запускается из-за чего-либо иначе (ожидание на мьютексе, сети и т. д.) считается бездействующим.

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

Что забрать? Возьмите iowait% числа от vmstat / iostat с зерном соли. Возможно, выиграть гораздо больше, чем быстрее подсистемы ввода-вывода (или правильной конфигурации), чем показывают цифры iowait. Рассматривайте iowait% + idle% ЦП как «доступный процессор», который потенциально может быть использован для улучшения подсистемы ввода-вывода и минимизации конфликтов

Чтобы проиллюстрировать это, давайте снова посмотрим на график процессора — теперь, когда я установил innodb_flush_log_at_trx_commit = 2

График загрузки процессора после оптимизации

Как видно из графика, мы восстановили гораздо больше, чем 6-7% времени простоя, которое у нас было — загрузка ЦП пользователя увеличилась до 70%, а idle + iowait теперь составляет 10-15%; пропускная способность системы увеличилась примерно на 60% — гораздо больше, чем позволяло бы «устранение iowait».