вторник, 1 сентября 2009 г.

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

Снова здорово. Народ, перешедший на ZFS, неожиданно для себя обнаружил малое количество свободной памяти в работающей системе и запаниковал.

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

Хорошо, повторенье - мать ученья, это особенно актуально в день Школия. :)

Показываю еще одну систему.

Это не прохладно работающая тыловая система, а фронтальный сервер, с Oracle EE, Apache, WebCache, сопутствующими сервисами и весьма активным ДНС впридачу.

root @ host / # top
load averages: 0.01, 0.01, 0.00; up 0+22:37:37 10:55:35
78 processes: 77 sleeping, 1 on cpu
CPU states: 99.1% idle, 0.4% user, 0.5% kernel, 0.0% iowait, 0.0% swap
Kernel: 301 ctxsw, 18 trap, 518 intr, 636 syscall, 17 flt
Memory: 4095M phys mem, 97M free mem, 4096M total swap, 4096M free swap

PID USERNAME NLWP PRI NICE SIZE RES STATE TIME CPU COMMAND
6472 oracle 11 59 0 0K 0K sleep 0:33 0.13% oracle
6467 oracle 11 59 0 0K 0K sleep 0:44 0.11% oracle
7382 root 1 59 0 20M 19M cpu/0 0:02 0.08% top
6412 oracle 1 59 0 0K 0K sleep 0:18 0.03% oracle
6400 oracle 17 59 0 0K 0K sleep 0:08 0.02% oracle
6392 oracle 1 59 0 0K 0K sleep 0:17 0.02% oracle
6454 ias1 6 59 0 61M 38M sleep 0:21 0.01% webcached
6408 oracle 1 59 0 0K 0K sleep 0:19 0.01% oracle
6402 oracle 20 59 0 0K 0K sleep 0:14 0.01% oracle
6603 oracle 35 59 0 251M 161M sleep 0:28 0.01% java
6452 ias1 6 59 0 25M 11M sleep 0:12 0.01% webcached
7262 oracle 1 59 0 8224K 2660K sleep 0:00 0.00% sshd
6474 oracle 3 59 0 0K 0K sleep 0:01 0.00% tnslsnr
6396 oracle 1 59 0 0K 0K sleep 0:01 0.00% oracle
315 root 1 100 -20 2552K 1240K sleep 0:02 0.00% xntpd

Как видно, свободной памяти почти нет. Однако:

root @ host / # swap -l
swapfile dev swaplo blocks free
/dev/zvol/dsk/rpool/swap 181,2 8 8388600 8388600

Неубедительно, правда? Взглянем детальней:

root @ host / # swap -s
total: 922904k bytes allocated + 840908k reserved = 1763812k used, 2641584k available

root @ host / # vmstat 5 5
kthr memory page disk faults cpu
r b w swap free re mf pi po fr de sr s0 -- -- -- in sy cs us sy id
0 0 0 2780460 303496 12 83 0 0 0 0 4 9 0 0 0 547 779 332 1 1 98
0 0 0 2584824 128200 0 18 0 0 0 0 0 6 0 0 0 520 630 290 0 0 99
0 0 0 2584548 127992 0 9 0 0 0 0 0 6 0 0 0 520 400 308 0 0 100
0 0 0 2584472 127884 0 0 0 0 0 0 0 2 0 0 0 504 324 277 0 0 99
0 0 0 2584464 127876 0 0 0 0 0 0 0 30 0 0 0 566 323 338 0 0 100

Подкачка, в общем, копеечная, как и показывает top.

Причем это в разгар рабочего дня, почти на пике нагрузок. О, вот потребовалась память:

load averages: 0.00, 0.01, 0.00; up 0+22:48:39 11:06:37
79 processes: 78 sleeping, 1 on cpu
CPU states: 99.2% idle, 0.3% user, 0.5% kernel, 0.0% iowait, 0.0% swap
Kernel: 287 ctxsw, 2 trap, 509 intr, 625 syscall, 2 flt
Memory: 4095M phys mem, 124M free mem, 4096M total swap, 4096M free swap

PID USERNAME NLWP PRI NICE SIZE RES STATE TIME CPU COMMAND
6472 oracle 11 59 0 0K 0K sleep 0:33 0.12% oracle
6467 oracle 11 59 0 0K 0K sleep 0:44 0.10% oracle
7427 root 1 59 0 3460K 2296K cpu/1 0:00 0.09% top
6412 oracle 1 59 0 0K 0K sleep 0:18 0.03% oracle
6392 oracle 1 59 0 0K 0K sleep 0:17 0.01% oracle
6408 oracle 1 59 0 0K 0K sleep 0:20 0.01% oracle
6402 oracle 20 59 0 0K 0K sleep 0:14 0.01% oracle
6603 oracle 35 59 0 251M 161M sleep 0:28 0.01% java
6454 ias1 6 59 0 61M 38M sleep 0:22 0.01% webcached
6452 ias1 6 59 0 25M 11M sleep 0:12 0.01% webcached
6400 oracle 17 59 0 0K 0K sleep 0:09 0.01% oracle
6541 ias0 16 59 0 19M 7840K sleep 0:04 0.00% opmn
6474 oracle 3 59 0 0K 0K sleep 0:01 0.00% tnslsnr
6552 ias0 3 59 0 25M 1832K sleep 0:12 0.00% httpd
7262 oracle 1 59 0 8224K 2660K sleep 0:00 0.00% sshd

"... и немедленно выпил."

Как видно, при запросах на память, ZFS сразу отступает, освобождая ARC в пользу приложения/процесса. Механизм preemption для ZFS закон.

Прошу обратить внимание на тот факт, что, хотя в подкачке и зарезервировано некоторое количество страниц, они туда фактически не попадают, так как ZFS возвращает страницы сканеру страниц раньше, чем происходит выгрузка в подкачку:

root @ host / # iostat 5 5
tty sd0 cpu
tin tout kps tps serv us sy wt id
0 38 508 9 12 1 1 0 98
0 22 8 1 0 0 0 0 100
0 8 20 2 0 0 0 0 100
0 8 48 5 0 0 0 0 100
0 8 8 1 0 0 0 0 100

Что при этом делает ZFS? Достаточно активно работает:

root @ host / # zpool iostat 5
capacity operations bandwidth
pool used avail read write read write
---------- ----- ----- ----- ----- ----- -----
data 12.1G 91.9G 1 5 183K 310K
rpool 5.95G 26.0G 0 0 13.5K 844
---------- ----- ----- ----- ----- ----- -----
data 12.1G 91.9G 0 1 0 51.1K
rpool 5.95G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.1G 91.9G 0 2 0 105K
rpool 5.95G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.1G 91.9G 0 17 0 502K
rpool 5.95G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.1G 91.9G 0 0 0 17.6K
rpool 5.95G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.1G 91.9G 0 0 0 16.0K
rpool 5.95G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.1G 91.9G 0 0 0 12.0K
rpool 5.95G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.1G 91.9G 0 0 0 16.0K
rpool 5.95G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.1G 91.9G 0 12 0 338K
rpool 5.95G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
data 12.1G 91.9G 0 25 0 1.02M
rpool 5.95G 26.0G 0 0 0 0
---------- ----- ----- ----- ----- ----- -----
^C

Так что, дорогие коллеги, успокаиваемся и не волнуемся. Солярис определенно писали не за еду. :)

Но самый сакраментальный вопрос мы-таки упустили из виду. Итак,

Почему ZFS так мечет оперативную память?

Чтобы ответить на этот вопрос, вспомним две вещи.

Первая при создании пула/датасета обычно не задают параметр recordsize, коей по умолчанию равен в подавляющем большинстве случаев 128К:

root @ host / # zfs get all|grep recordsize
rpool recordsize 128K default
rpool/ROOT recordsize 128K default
rpool/ROOT/zfsBE_sol10_509 recordsize 128K default

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

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

Если в системе только один root pool, то, как показывает практика, существенной нагрузки на память нет, что вполне объяснимо. Прочитали данные в память один раз, ZFS из кэша данные выкинула и освободила его по таймауту.

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

А вот если есть второй пул, на котором стоит Oracle - другое дело. Oracle читает большими предвыборками, мультиблоком (db_file_multiblock_read_count), если процент scattered reads большой, то, соответственно, ARC буферизует предвыборку, особенно когда обнаруживает предсказуемую и устойчивую склонность приложения к повторяющимся большим предвыборкам данных с диска.

Вот и вся причина заполнения памяти в случае, если на ZFS стоит Oracle.

Вопрос. Насколько это критично?

Опыт показывает, что работа СУБД как минимум не замедляется, а, при необходимости освободить означенную память, занятую кэшем ARC, это делается немедленно, как и было сказано выше.

Можно ли уменьшить расход памяти под кэш ZFS?

Если вас это беспокоит - можно.

Во-первых, можно создавать пулы под БД с отдельными логами и кэшами на дисках. Рекомендую перед этим все же внимательно прочесть вот это. Вдумчиво курим маны zpool:

log A separate intent log device. If more than one log
device is specified, then writes are load-balanced
between devices. Log devices can be mirrored. How-
ever, raidz and raidz2 are not supported for the
intent log. For more information, see the "Intent
Log" section.

cache A device used to cache storage pool data. A cache
device cannot be cannot be configured as a mirror
or raidz group. For more information, see the
"Cache Devices" section.

Во-вторых, можно до заполнения пулов поиграть с их свойством recordsize. Уменьшение данного свойства, скажем, до 16К, уменьшает нагрузку на память, даже в случае использования БД. Данная рекомендация, однако, вступает в некоторое противоречие вот с этими рекомендациями от инженеров Сан, смотреть Oracle Considerations - там есть пара взаимоисключающих параграфов, которые могут вызвать когнитивный диссонанс:

  • For better OLTP performance, match the ZFS recordsize to the Oracle db_block_size.
  • Use a separate file system with the default 128K record size for Oracle logs.
Заметьте, что, с точки зрения, скажем, БД, предпочитающей select или работающей с LOB (реально приличного размера) размер записи в 128К в целом предпочтительней пулов с меньшими значениями размеров записи.

В-третьих, можно просто прикупить еще памяти и не беспокоиться. :)

ZFS ведет себя вовсе не как Windows - "... сколько находит - столько и занимает". Сколько нужно - столько и займет. И отдаст немедленно по первому требованию.