Архив метки: freebsd

Блокирование процесса MySQL-сервера в оперативной памяти. Memlock на FreeBSD.

Бывают случаи, когда надо запретить операционной системе сервера переносить данные определенного сервиса/приложения в swap. Т.е. все остальное можно, хоть и нежелательно, а вот какое-нибудь одно ну совсем нельзя. Для этого во всех (ну или почти во всех) современных unix системах существует системный вызов memlockall — он отвечает как раз за «блокировку» данных в оперативной памяти. Можно, конечно, долго рассуждать о том, что если swap на сервере становится нужен — пора апгрейдить сервер/снижать нагрузку на него. Так и обстоят дела в идеальном мире.

Но наш мир далек от совершенства и в ПО, в том числе системном, тоже бывают ошибки. Я столкнулся с одной пренеприятнейшей ситуацией на сервере под управлением FreeBSD 9.2, с корнем на ZFS. Ситуация состояла в том, что система никак не хотела освобождать память из-под ARC-кэша, когда она была необходима приложениям и предпочитала сбрасывать данные других приложений в swap. Все бы ничего, но самым «жрущим» процессом всегда оказывался MySQL, половину которого она и умудрялась выдавить в swap.

А потом начиналось самое интересное: в те лохматые времена, когда устанавливался этот сервер, про размещение swap на zfs никто ничего плохого не говорил. А потом выяснили, что внезапно при активном чтении/записи из свапа (например, при внезапно возросшей нагрузке с истребованием данных mysql, которые упали в свап и вытеснением других приложение туда же) может возникнуть deadlock. Выглядит это очень весело, но не в том случае, если сервер не имеет удаленного управления и стоит в далеком дата-центре, в который надо ехать, да еще и согласовав свой приезд с сотрудниками заранее. С виду сервер остается живым — отвечает на пинги, принимает пакеты на открытые порты, но не отдает никаких данных. При подключении клавиатуры бодро выкидывает в консоль сообщение о подключении девайса, но не реагирует на нее и, более того, не пишет никакие логи. Помогает только reset.

Если у вас действительно не хватает памяти на сервере, то установка «—memlock» не поможет и будет даже вредной. При исчерпании свободной памяти и отсутствии возможности «вынести» кого-нибудь в swap, система с большой долей вероятности просто убьет самый объемный процесс, которым окажется как раз mysql

Впрочем, я отвлекся. Целью этого повествования является как раз способ принуждения системы «убрать руки» от памяти, выделенной для 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 — скрипт может быть перезаписан в процессе обновления.

Горячие бэкапы mysql при помощи снэпшотов ZFS в FreeBSD

Давным-давно, на заре времен, когда сервера были большими, а базы данных маленькими, все нормальные люди бэкапили их просто при помощи mysqldump. Получался в результате сего действия вполне себе хороший бэкап, который можно восстановить где угодно. Шло время, объемы баз росли… В какой-то момент придумали mysqlhotcopy для того, чтобы не тратить ресурсы на полное вычитывание базы (с блокировкой таблиц на это время). Он прекрасно справлялся с таблицами myisam, но с innodb ничего поделать не мог. Долгое время «горячий» бэкап innodb-таблиц был возможен только при помощи платного софта, который не все могли себе позволить. Потом добрая Percona выпустила свой бесплатный xtrabackup, который снимал горячие копии InnoDB, XtraDB и MyISAM таблиц. Вроде бы все было хорошо, но… Если ваш сервер не относится к high-end, но очень старается работать также (с минимальными перерывами в обслуживании), то даже бэкап с помощью xtrabackup на продакшене может вызвать небольшой даунтайм — если вы копируете базы размером в 5-10 Гб, а запросы к ним идут хотя бы пару раз в секунду. Конечно, можно сказать, что мир неидеален и надо ставить второй сервер, настраивать репилкацию и снимать копии со слэйва. Но не всегда на это выделяется должное финансирование.

Всю ситуацию может спасти сервер с FreeBSD на ZFS. В FreeBSD поддержка ZFS сделана уже довольно давно и работает она в ней стабильно, хоть и намного медленнее, чем в Solaris. Именно в ZFS есть модный нынче «механизм» снэпшотов (снимков) файловой системы. Причем сделано все так, что наличие большого количества снэпшотов никак не влияет на производительность сервера (кроме, конечно, поедания дискового пространства). Именно при помощи снэпшотов можно делать самые быстрые горячие бэкапы mysql. Но тут всплывает один нюанс: InnoDB сбрасывает «грязные» страницы на диск не сразу, да и мы можем сделать снэпшот как раз в момент записи данных (и чем больше пишем, тем больше шансов это сделать). Т.е. наша база в бэкапе может оказаться с битыми таблицами. Приятного мало.

Можно, конечно, набросать простенький скрипт, который будет делать «flush tables with read lock», потом снэпшот, потом «unlock tables», но лучше воспользоваться уже готовым вариантом. В FreeBSD портировали прекрасную софтинку (или даже скрипт) для автоматического создания снэпшотов — zfstools (искать тут: /usr/ports/sysutils/zfstools).  Ей необходимо передавать всего 2 параметра: название серии снэпшотов и количество снэпшотов, которые необходимо в этой серии хранить. Запускается она так:

/usr/local/sbin/zfs-auto-snapshot hourly 72

Объяснить ей что надо снэпшотить, а что нет, очень просто: необходимо выставить свойство com.sun:auto-snapshot = true для конкретной ФС в zpool (причем этот параметр наследуется всеми «дочерними» ФС). При запуске она просматривает все файловые системы в zpool и снэпшотит те из них, в которых свойство  com.sun:auto-snapshot установлено не в false. В случае снэпшота файловой системы, в которой лежат базы данных (вы же создали под них отдельную ФС, верно?) вместо true необходимо написать mysql. Скрипт автоматически выполнит команду «flush tables with read lock», сделает снэпшот и разблокирует таблицы. Таким образом, скорость выполнения будет напрямую зависеть от количества данных, которые необходимо сбросить на диск при снятии снимка.

Но опытные пользователи админы понимают: снэпшот — не замена бэкапа. Если умрет сервер — умрут и все снэпшоты. Для простого доступа к файлам снэпшотов, в FreeBSD реализован замечательный «скрытый» путь. Если мы сделали снэпшот /var/db/mysql при помощи zfs-auto-snapshot 1 марта 2015 года в 11:00, то получить к нему доступ сможем по пути: /var/db/mysql/.zfs/snapshot/zfs-auto-snap_hourly-2015-03-01-11h00. Таким образом, сразу после снятие снэпшота, его можно скопировать куда угодно каким угодно способом. А для того, чтобы не создавать лишнюю нагрузку на диски, лучше всего копировать оттуда данные при помощи rsync — после первого копирования мы будем перемещать только измененные таблицы, что чаще всего значительно уменьшает время, необходимое на копирование и неплохо снижает нагрузку на диск. И уже после этого, в удаленном месте (это может быть даже виртуалка на обычном PC) снимаем бэкапы.