Бывают случаи, когда надо запретить операционной системе сервера переносить данные определенного сервиса/приложения в swap. Т.е. все остальное можно, хоть и нежелательно, а вот какое-нибудь одно ну совсем нельзя. Для этого во всех (ну или почти во всех) современных unix системах существует системный вызов memlockall — он отвечает как раз за «блокировку» данных в оперативной памяти. Можно, конечно, долго рассуждать о том, что если swap на сервере становится нужен — пора апгрейдить сервер/снижать нагрузку на него. Так и обстоят дела в идеальном мире.
Но наш мир далек от совершенства и в ПО, в том числе системном, тоже бывают ошибки. Я столкнулся с одной пренеприятнейшей ситуацией на сервере под управлением FreeBSD 9.2, с корнем на ZFS. Ситуация состояла в том, что система никак не хотела освобождать память из-под ARC-кэша, когда она была необходима приложениям и предпочитала сбрасывать данные других приложений в swap. Все бы ничего, но самым «жрущим» процессом всегда оказывался MySQL, половину которого она и умудрялась выдавить в swap.
А потом начиналось самое интересное: в те лохматые времена, когда устанавливался этот сервер, про размещение swap на zfs никто ничего плохого не говорил. А потом выяснили, что внезапно при активном чтении/записи из свапа (например, при внезапно возросшей нагрузке с истребованием данных mysql, которые упали в свап и вытеснением других приложение туда же) может возникнуть deadlock. Выглядит это очень весело, но не в том случае, если сервер не имеет удаленного управления и стоит в далеком дата-центре, в который надо ехать, да еще и согласовав свой приезд с сотрудниками заранее. С виду сервер остается живым — отвечает на пинги, принимает пакеты на открытые порты, но не отдает никаких данных. При подключении клавиатуры бодро выкидывает в консоль сообщение о подключении девайса, но не реагирует на нее и, более того, не пишет никакие логи. Помогает только reset.
Впрочем, я отвлекся. Целью этого повествования является как раз способ принуждения системы «убрать руки» от памяти, выделенной для MySQL. Для этого существует параметр запуска —memlock. Первое решение, которое приходит в голову — прописать в /etc/rc.conf строчку:
mysql_args="--memlock"
Прописываем, перезапускаем mysql и видим (с помощью запроса show variables like ‘%lock%’), что переменная locked_in_memory выставлена в OFF.
Начинаем думать и читать мануалы… Оказывается, для успешного выполнения вызова memlockall (который и выполняется при указании параметра —memlock), необходимы root-привилегии. Но мы же не хотим, чтобы сервер работал от имени root? В случае с FreeBSD без редактирования стартового скрипта mysql, к сожалению, не обойтись — пользователь ‘mysql’ туда «захардкожен» и его придется сменить ручками. Открываем /usr/local/etc/rc.d/mysql-server и вместо mysql_user=»mysql» вписываем mysql_user=»root». Далее обязательно удаляем из строки:
command_args="-c -f /usr/local/bin/mysqld_safe --defaults-extra-file=${mysql_optfile} --user=${mysql_user} --datadir=${mysql_dbdir} --pid-file=${pidfile} ${mysql_args}"
кусочек «—user=${mysql_user}». Не забываем, что в rc.conf у нас остался mysql_args=»—memlock». И, наконец, для того, чтобы пользователь после запуска сменился на mysql, указываем в файле /etc/my.cnf (по умолчанию его нет в системе) следующее:
[mysqld] user=mysql
Важно указать это именно в /etc/my.cnf — этот файл читается первым, а mysql в целях безопасности применяет только первую указанную опцию user. После проделанных манипуляций перезапускаем mysql-server командой:
/usr/local/etc/rc.d/mysql-server restart
и проверяем переменную «locked_in_memory» — она должна быть ON. Также, вы сразу заметите увеличение количества wired memory в выводе top на объем, занятый mysql.
В моем случае это проблему решило и, как ни странно, система перестала уходить в swap — память при необходимости отбиралась у ARC, хотя других изменений не производилось.
P.S. Главное не забыть об этих изменениях после обновления mysql — скрипт может быть перезаписан в процессе обновления.