среда, 2 сентября 2009 г.

Оперативная память и ZFS II

В предыдущей статье я рассказывал, почему ZFS так зверски задействует оперативную память под ARC. Сейчас мы попробуем выяснить несколько моментов - в частности, нельзя ли как-то улучшить положение в плане латентности Соляриса при запросах на память, занятую кэшами ZFS, более эффективно использовать дисковое пространство пулов, немного поиграть с предвыборкой да и вообще - возможен ли хоть какой-то тюнинг ZFS или, как говорит Sun - "Тюнинг есть зло"?

Дело в следующем. Как и было сказано, параметр recordsize у ZFS по умолчанию 128К. Это, в общем, сделано с умыслом - "Мы объединяем диски, а не делим их" - и речь идет о действительно больших сториджах. Однако такой большой размер записи предполагает, что и предвыборка будет выполняться как минимум такими порциями. Если, скажем, у нас на руках директория с большим количеством мелких файлов на пуле с таким размером записи - кэш потребуется примерно вдвое (грубо) больше, чем реально требуется для всех этих файлов вместе взятых.

Кроме того, есть еще одно соображение. В предыдущей статье я сказал, что память кэша освобождается по запросу. Это действительно так. Однако - как быть с приложениями, которые часто и интенсивно запрашивают память? Будет ли здесь играть роль тот факт, что ZFS еще нужно освободить память, а перед этим выполнить хоть небольшой - но чекпойнт?

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

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

Проблема, как ни странно, заключается в гуляющих по интернету примерах, где ZFS-пулы создаются, прямо скажем, с минимальным числом параметров. Кои включают в себя все умолчания, среди которых и recordsize=128K.

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

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

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

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

Вчера, по следам прошлой статьи, я провел эксперимент с описанным в статье сервером (с той же самой машиной) , и раздробил пул, на котором стоят ораклиные приложения, на датасеты, с созданием некоей иерархии файловых систем с раздельной установкой параметра recordsize.

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

Вот статистика работающей системы с прогретым кэшем:

load averages: 0.01, 0.04, 0.08; up 0+00:16:14 12:29:55
76 processes: 75 sleeping, 1 on cpu
CPU states: 99.4% idle, 0.1% user, 0.5% kernel, 0.0% iowait, 0.0% swap
Kernel: 297 ctxsw, 2 trap, 499 intr, 240 syscall, 2 flt
Memory: 4095M phys mem, 1937M free mem, 4096M total swap, 4096M free swap

PID USERNAME NLWP PRI NICE SIZE RES STATE TIME CPU COMMAND
613 root 1 59 0 6228K 5072K cpu/0 0:00 0.06% top
430 oracle 1 59 0 0K 0K sleep 0:00 0.03% oracle
532 oracle 35 59 0 235M 120M sleep 0:12 0.02% java
426 oracle 1 59 0 0K 0K sleep 0:00 0.01% oracle
400 ias1 6 59 0 25M 11M sleep 0:00 0.01% webcached
402 ias1 6 59 0 56M 33M sleep 0:00 0.01% webcached
410 oracle 1 59 0 0K 0K sleep 0:00 0.01% oracle
420 oracle 11 59 0 0K 0K sleep 0:00 0.01% oracle
463 ias0 16 59 0 19M 7824K sleep 0:00 0.00% opmn
428 oracle 1 59 0 0K 0K sleep 0:00 0.00% oracle
477 ias0 3 59 0 25M 1728K sleep 0:00 0.00% httpd
446 oracle 3 59 0 0K 0K sleep 0:00 0.00% tnslsnr
536 oracle 1 59 0 8224K 2616K sleep 0:00 0.00% sshd
418 oracle 17 59 0 0K 0K sleep 0:00 0.00% oracle
303 root 1 100 -20 2552K 1224K sleep 0:00 0.00% xntpd

В сравнении со вчерашними цифрами картина заметно другая. Более того, отклик стал лучше почти в полтора-два раза (субъективно).

Что реально было сделано? Всего лишь создано 18 датасетов:

oracle @ host ~ $ zfs list
NAME USED AVAIL REFER MOUNTPOINT
data 12.0G 90.4G 96K /export/home
data/OraHome1 4.70G 90.4G 4.70G /export/home/OraHome1
data/OraHome2 788M 90.4G 788M /export/home/OraHome2
data/OraHome3 501M 90.4G 501M /export/home/OraHome3
data/OraHome4 163M 90.4G 163M /export/home/OraHome4
data/apex 641M 90.4G 641M /export/home/apex
data/awstats 37.0M 90.4G 37.0M /export/home/awstats
data/db 5.19G 90.4G 18K none
data/db/flash_recovery_area 157M 90.4G 157M /export/home/flash_recovery_area
data/db/oradata 4.82G 90.4G 4.82G /export/home/oradata
data/db/plsql_libs 217M 90.4G 217M /export/home/oradata/plsql_libs
data/users 281K 90.4G 18K none
data/users/ias0 29K 90.4G 29K /export/home/ias0
data/users/ias1 43.5K 90.4G 43.5K /export/home/ias1
data/users/oracle 190K 90.4G 190K /export/home/oracle
rpool 9.96G 21.5G 41.5K /rpool
rpool/ROOT 3.96G 21.5G 18K /rpool/ROOT
rpool/ROOT/zfsBE_s10_509 3.96G 21.5G 3.96G /
rpool/dump 2.00G 21.5G 2.00G -
rpool/swap 4G 25.5G 16K -

Параметр recordsize установлен для датасетов следующим образом:

root @ host / # zfs get recordsize
NAME PROPERTY VALUE
data recordsize 128K
data/OraHome1 recordsize 64K
data/OraHome2 recordsize 64K
data/OraHome3 recordsize 64K
data/OraHome4 recordsize 64K
data/apex recordsize 16K
data/awstats recordsize 16K
data/db recordsize 128K
data/db/flash_recovery_area recordsize 128K
data/db/oradata recordsize 128K
data/db/plsql_libs recordsize 16K
data/users recordsize 16K
data/users/ias0 recordsize 16K
data/users/ias1 recordsize 16K
data/users/oracle recordsize 16K
rpool recordsize 128K
rpool/ROOT recordsize 128K
rpool/ROOT/zfsBE_s10_509 recordsize 128K
rpool/dump recordsize -
rpool/swap recordsize -

Что в сухом остатке?

А в сухом остатке следующие выводы:

  1. Создавать датасеты на ZFS-пулах можно и нужно.
  2. Умная Маша, еще на этапе создания пула, почешет репу и подумает, под что он - и ДО заполнения установит нужные параметры хранения пула, прежде всего, recordsize. Это, кстати говоря, касается и root pool. Размер записи в 128К может оказаться реально великоват для конкретной файловой системы - в плане расхода памяти под ARC прежде всего. Соответственно, мы также увидели, что это тюнинговый параметр. Собственно, такой же, как и размер кластера или блока для RAID, кто не въехал.
  3. Кроме оптимизации, дробление на датасеты (в том числе иерархические - для хоть сколько-нибудь сложных либо продвинутых систем хранения) - отличная административная практика. Снапшоты, клонирование, горячее реструктурирование, выборочные бэкапы итп. Собственно, все то же самое, для чего и предназначены отдельные файловые системы с точками монтирования. Кстати говоря, точки монтирования в ZFS допускают абсолютно гибкое жонглирование, хоть вообще не задавай для родительских ФС маунтпойнты.
Что ж, коллеги. Тщательней планируйте сторидж - и будет вам счастье. И быстро, и удобно, и ресурсы экономите.

PS. Кстати говоря, объем операций IO снизился заметно:

root @ host / # zpool iostat 5
capacity operations bandwidth
pool used avail read write read write
---------- ----- ----- ----- ----- ----- -----
data 12.0G 92.0G 0 8 0 189K
rpool 5.96G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.0G 92.0G 0 1 0 24.0K
rpool 5.96G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.0G 92.0G 0 0 0 7.98K
rpool 5.96G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.0G 92.0G 0 15 0 610K
rpool 5.96G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.0G 92.0G 0 2 0 55.9K
rpool 5.96G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.0G 92.0G 0 0 0 7.98K
rpool 5.96G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.0G 92.0G 0 0 0 16.0K
rpool 5.96G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.0G 92.0G 0 2 0 35.1K
rpool 5.96G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.0G 92.0G 0 1 0 25.5K
rpool 5.96G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.0G 92.0G 0 13 0 473K
rpool 5.96G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
^C

Что, в общем, говорит о том, что мне не померещилось.