Как сделать стабильный MySQL на малых объемах памяти

По моим наблюдениям, куча народу мучается с МуSQL на мелких виртуалках, ибо он мало что прожорлив на память, так еще и имеет привычку падать по ее нехватке. Решение у этой проблемы есть, но самое популярное «решение» от Гугля на самом деле не работает. Что работает — читаем дальше.

Проблема в том, что MySQL в последних версиях невероятно прожорлив на память, и, что особенно печально, никакими параметрами конфигурации невозможно заставить его  вести себя иначе. Нынче малые VPS можно купить за сущие копейки, и многие переводят свои небольшие проекты с shared хостинга на VPS. И вот тут начинаются скачки по граблям, одними из самых мощных является постоянное падение MySQL. Причем если старые версии падают и лежат, приводя к неработоспособности сервиса, то 5.7 сама падает, сама встает, создавая иллюзию жизнеспособности.

Первое, что советуют везде — урезать параметр innodb_buffer_pool_size, который по умолчанию равен 128М. Урезание его до 64 или даже до 32М несколько стабилизирует работу системы, т.е. падения случаются существенно реже. Но это — до первой нагрузки, поскольку память остается «слабым местом», высвободилось ее немного.

Более радикальное решение этой проблемы, приводящее к полной стабилизации системы в том, чтобы добавить swap, увеличив количество доступной памяти, как это рекомендует делать DigitalOcean. Сделать это, увы любителям всяких контрольных панелей, можно только из консоли сервера с правами root.

Итак, пошагово, для линукс-систем на базе RedHat, то есть все версии CentOs, Ubuntu и так далее. Хотя по идее и на других дистрибутивах должно работать. Все команды от root. Подразумеваем, что Вы умеете пользоваться sudo, потому что обычный вход от рута — очень плохая практика.

1. создаем файл нужного размера

dd if=/dev/zero of=/swapfile bs=1024 count=1024k

Здесь count — число блоков по 1024 байта, т.е. по сути размер свопа в килобайтах. В примере задан 1 гиг. Имя файла — swapfile, расположение — корневой каталог файловой системы.

2. Устанавливаем права правильно

chmod 0600 /swapfile

3. Форматируем этот файл именно своп-файлом

mkswap /swapfile

4. Подключаем своп

swapon /swapfile

Теперь если мы посмотрим состояние свопа командой «swapon -s», то мы увидим:

[root@pavlyuts /]# swapon -s
Filename                                Type            Size    Used    Priority
/swapfile                               file            1048572 0       -1
[root@pavlyuts /]#

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

5. Чтобы своп пережил перезагрузку необходимо внести его в таблицу разделов. Любым доступным Вам редактором необходимо открыть файл /etc/fstab и добавить туда строку:

/swapfile       none    swap    sw      0       0

6. Отдельный тонкий момент: мы расширили память системы за счет свопа, но чтобы не убить производительность нам надо сказать ядру, что своп — он на самый черный день, если памяти совсем не хватает. Политикой выгрузки страниц памяти в своп управляет параметр swappiness, обозначающий число процентов свободной памяти при которых надо начинать использовать своп. Корифеи рекомендуют в нашем случае установить его в 10.

echo 10 | tee /proc/sys/vm/swappiness

И добавить параметр в скрипт чтобы не потерять при перезагрузке.

echo vm.swappiness = 10 | tee -a /etc/sysctl.conf

Все, доступная память системы увеличилась на 1 гигабайт «резерва».

Конкретно для моего сервера 1CPU/1G/SSD, на котором крутится вот этот самый блог, в «состоянии покоя» результат выглядит вот так:

[pavlyuts@pavlyuts /]$ free
              total        used        free      shared  buff/cache   available
Mem:        1016908      480160      198684        7264      338064      392560
Swap:       1048572           0     1048572

Казалось бы, своп не используется и мы сделали что-то совершенно бессмысленное. Однако стоит как следует нагрузить его запросами (например, задать с другого хоста ab -n 500 -c 20 https://pavlyuts.ru/), то ситуация меняется радикально, на скриншоте самые большие значения использования в серии.

[pavlyuts@pavlyuts /]$ free
              total        used        free      shared  buff/cache   available
Mem:        1016908      828448      119716         200       68744       89828
Swap:       1048572      467812      580760

Как видим, в пике расходуется еще почти полгигабайта памяти. При этом отдача страниц, естественно, нещадно тупит, но система сохраняет работоспособность. Как показали предыдущие эксперименты, при попытке дать такую нагрузку без свопа MySQL падает независимо от ухищрений в конфигурации, здесь же просто следующим «узким местом» стал процессор.

Еще одним популярным решением является использования MariaDB вместо MySQL. Не смотря на то, что Мария намного более экономно использует память, при больших нагрузках и малом количестве памяти результат будет тот же. Поэтому для MariaDB данный способ так же рекомендован.

P.S. Благодаря умелым рукам и умной голове Дмитрия, которому огромное спасибо за тюнинг, по состоянию на сегодня в связке nginx/php-fpm/mysql вообще без кэширования и прочих ухищрений домашняя страница отдается со скоростью где-то 2,5 rps, статика — около 42 rps. это соответствует как минимум 5..7 тыс. запросов в час. И все это на сервере за 200 рублей в месяц. Осталось стать настолько популярным!