Предистория

В какой-то момент мы переезжали с Amazon AWS на Google Cloud. На Амазоне мы использовали ElastiCache в количестве 5 отдельных экземпляров, без кластеров и т.д. Бэкапы делались автоматически, иногда вручную (при необходимости), но все равно с помощью средств, предоставляемых Амазоном.

В процессе переезда и тестирования возможностей GCE встал вопрос о поднятии инстансов Redis самостоятельно и настройки создания резервных копий баз, поскольку Google не предоставляет сервисов, аналогичных ElastiCache.

Redis Persistence

Redis позволяет сохранять данные на диск следующими способами:

  • RDB: это point-in-time снапшоты данных, создаваемые с определенным интервалом
  • AOF: файл, в который записывается каждая операция на запись, полученная сервером. Команды записываются в том же формате, что и сам протокол Redis, в режиме append-only, то есть только добавление.

Если необходимость сохранения данных отсутствует, все перечисленное можно отключить. Так же возможны комбинация обеих параметров.

Плюсы и минусы RDB и AOF

RDB

Самый простой способ делать резервные копии данных. Благодаря сжатию, которые можно включить в конфиге, занимает гораздо меньше места, чем на диске AOF. Представляет собой единый файл, который обновляется периодически автоматически, если в конфиге указан параметр SAVE, или при необходимости с помощью команд SAVE или BGSAVE.

Поскольку файл один и достаточно компактного размера, его удобно отправлять на хранение куда-нибудь в облако, например, Amazon S3 или Google Cloud Storage. Плюс, по сравнению с AOF, позволяет быстрее загрузить данных в память при восстановлении или рестарте сервиса, особенно, если размер данных большой.

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

AOF

AOF это аббревиатура от Append Only File, а это означает то, что Redis не изменяет уже записанные данные, а лишь добавляет новые в конец.

Впрочем, AOF также использует fork(), но в данном случае, можно настроить, как часто писать в лог.

Для AOF можно настроить политики fsync: каждую секунду, на каждый запрос или вообще ничего не делать. Благодаря тому, что при использовании AOF Redis по умолчанию пишет данные на диск каждую секунду, максимум, что вы теряете в случае сбоя при использовании этого режима — это 1 секунда. Redis может автоматически перезаписывать AOF-файл, если он становится слишком большим. Но у AOF тоже есть недостатки.

Обычно файлы AOF гораздо больше по размеру, чем аналогичный файл RDB, при том же наборе данных. AOF может быть медленнее, чем RDB на запись, в зависимости от настроек fsync. Кроме того, даже в случае большой нагрузки на запись RDB лучше ведет себя в плане задержек.

Более подробно можно почитать здесь.

ElastiCache Backup > S3 bucket

Маленькая инструкция на тему того, как это сделать:

  1. В панели правления ElastiCache выбрать Backups.
  2. Из списка резервных копий выбрать нужную для экспорта.
  3. Выбрать Copy.
  4. В Create a Copy of the Backup?
  • В New backup вписать имя файла резервной копии. Имя должно содержать от 1 до 100 символов и быть в кодировке UTF-8. При сохранении будет добавлен идентификатор инстанса ElastiCache и .rdb. Например, если назвать my-exported-backup, будет создан файл my-exported-backup-0001.rdb.
  • Из списка Target S3 Location выбрать нужный бакет.
  • Выбрать Copy.

Стоит уточнить, что у ElatiCache должен быть доступ на запись в этот бакет. Сохранение в какую-либо “директорию” внутри бакета не поддерживается, сохраняется прямо в корень. Возможно, это можно сделать, если совсем уж необходимо, используя AWS CLI, но я не проверял. В любом случае, перед процедурой стоит прочитать соответствующий раздел в документации.

Состояние после переезда на GCE

Настройки Redis переносили с ElastiCache один в один. Сделали резервную копию вручную, сохранив дамп в бакете на S3.

На GCE подняли инстанс с Redis. Конкретные настройки из файла конфигурации указывать не буду, поскольку сильно зависит от того, как используется. Настройки ОС на инстансе были сделаны с учетом рекомендаций, описанных в документации Redis: https://redis.io/topics/admin.

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

Использование BGSAVE

Очень отличная штука. Запускается таким образом:

➔︎ redis-cli bgsave

Работает это следующим образом. Redis делает fork, а потом дочерний процесс записывает снапшот на диск, пока основной процесс продолжает отвечать на команды. Все работает отлично, но… Есть нюансы.

Если операции записи относительно небольшие, все работает, как и описано. А вот если запись ведется очень активно, то… То стоит перечитать еще раз этот документ и обратить внимание на следующее:

If you are using Redis in a very write-heavy application, while saving an RDB file on disk or rewriting the AOF log Redis may use up to 2 times the memory normally used. The additional memory used is proportional to the number of memory pages modified by writes during the saving process, so it is often proportional to the number of keys (or aggregate types items) touched during this time. Make sure to size your memory accordingly.

Именно это у нас и произошло. При тестах на QA все работало отлично, на продакшене же срабатывал OOM killer. Попытки уменьшить в настройках использование сервисом памяти через maxmemory, не увеличивая количество памяти на инстансе, успехом не увенчались.

Использование SAVE

Если простой системы какое-то время не так страшен, можно использовать команду SAVE. Здесь принцип немного другой.

При вызове этой команды запускается создание снапшота, но в этот раз Redis прекращает обработку любых/всех полученных команд до тех пор, пока создание снапшота не будет завершено. Обычно эта команда не используется, если только время простоя не важно или в системе отсутствует достаточное количество памяти для BGSAVE.

Стоит заметить, что параметр save, который указывается в файл конфигурации Redis, например, save 60 1000, запускает также команду BGSAVE.

Использование AOF

При записи в файл нагрузка небольшая, по крайней мере, на тестовом сервере на графиках она была незаметна. Но… Здесь тоже есть “но”, как и в остальным вариантах. Redis автоматически пересоздает файл, если он становится слишком большим. Возьмем пример из документации:

Например, если вы увеличиваете счетчик 100 раз, вы в конечном итоге получите единственный ключ в наборе данных, содержащий окончательное значение, но при этом вы будете иметь 100 записей в файле AOF. 99 из них не нужны для того, чтобы пересоздать текущее состояние.

Redis умеет пересоздавать файл в бэкграунде, не прерывая обслуживание клиентов. Всякий раз, когда вызывается команда BGREWRITEAOF, Redis записывает короткую последовательность команд, необходимых для пересоздания состояния набора данных, находящихся в памяти. Начиная с версии 2.4 команда вызывается автоматически и нет необходимости запускать ее вручную.

Восстановление из AOF занимает чуть больше времени, чем из RDB, но в нашем случае это было совсем не существенно.

Некоторые итоги

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

Каждый волен выбирать наиболее подходящий для себя вариант и использовать его или же не использовать никакого.