Резервное копирование
Мы используем Redis для построения пользовательских фидов. Восстановление всех фидов с нуля в случае потери данных занимает достаточно большое количество времени, поэтому мы делаем бекапы. Даже если вы используете Redis в качестве кэширующего сервера, в некоторых случаях прогрев кэша может быть долгим. Поэтому всегда рекомендуется делать резервные копии.
Redis делает RDB-снапшоты на основе следующих параметров в redis.conf:
save 900 100 save 300 1000 save 60 100000 dir /var/redis/ dbfilename dump.rdb rdbcompression yes
Существует два persistence-режима: RDB и AOF. Стоит отметить, что в обоих режимах используется весьма надёжный способ записи информации на диск, который практически исключает ситуации потери данных при аппаратном сбое. Как много данных вы потеряете зависит лишь от выбора persistence-режима. RDB позволяет регулировать этот параметр гибко, но в среднем, при сбое, может быть потеряно около часа. В этом режиме Redis сначала пишет полный снапшот базы во временный файл и только после окончания записи на диск переименовывает его в рабочий. Это исключает потерю данных благодаря атомарности системного вызова rename().
В случае с AOF, Redis ведёт лог операций, которые выполняют клиенты и записывает их в файл (по умолчанию каждую секунду). AOF это аббревиатура от Append Only File, а это означает то, что Redis не изменяет уже записанные данные, а лишь добавляет новые в конец. Благодаря тому, что при использовании AOF, Redis по умолчанию пишет данные на диск каждую секунду, максимум, что вы теряете в случае сбоя при использовании этого режима — это 1 секунда.
В проекте у нас используется RDB потому что час данных для нас потерять некритично. Кроме того, в худшем случае, данные могут быть восстановлены из основной СУБД.
Подробнее про persistence в Redis:
http://redis.io/topics/persistence
http://antirez.com/post/redis-persistence-demystified.html
Для бэкапов мы используем замечательный backup gem, в котором есть поддержка Redis.
# encoding: utf-8 Backup::Model.new(:my_backup, 'My Backup') do split_into_chunks_of 500 database Redis do |db| db.name = "dump" db.path = "/var/redis" db.host = "localhost" db.additional_options = [] db.invoke_save = false end compress_with Gzip do |compression| compression.best = true compression.fast = false end notify_by Mail do |mail| # ... end store_with S3 do |s3| # ... end end
Мы специально отключаем invoke_save в конфиге потому что не хотим, чтобы основной поток Redis заблокировался записью на диск при каждом бэкапе.
Мониторинг
Для мониторинга используется monit, который настроен уведомлять о всех происходящих событиях. Настройки для него достаточно просты:
set mailserver localhost set mail-format { from: monit-app1@example.com } set alert support@example.ru but not on { action pid ppid } check process redis with pidfile /var/run/redis.pid start program = "/usr/bin/redis-server /etc/redis/redis.conf" stop program = "/usr/bin/redis-cli -p 6379 shutdown" group redis
Нехватка памяти при BGSAVE
При больших объёмах хранимых данных возможны проблемы в случае использования RDB-снапшотов. Для записи используется команда BGSAVE, которая делает форк текущего процесса и в этом форке данные записываются на диск. Таким образом, основной поток не блокируется и запись происходит асинхронно. Проблема в том, что в UNIX-системах при вызове fork(), в дочерний процесс так же копируется содержимое памяти, которую использует родительский процесс. Допустим, если Redis в текущий момент времени занимает 2Gb памяти, а в системе остался только 1Gb свободной памяти, то при выполнении команды BGSAVE возможна следующая ошибка:
[18696] 28 Mar 12:26:54 # Can't save in background: fork: Cannot allocate memory
В современных системах при копировании памяти для форков используется метод Copy on Write. Память копируется только тогда, когда происходит запись в соответствующий участок. Redis делает форк процесса лишь для того, чтобы сохранить данные асинхронно, этот форк их никак не изменяет, а значит мы можем спокойно установить системный параметр vm.overcommit_memory в значение 1. Этот параметр отвечает за возможность выделения большего объёма памяти, чем доступно. Добавляем в /etc/sysctl.conf строчку:
vm.overcommit_memory = 1
И перечитывам конфиг:
# sysctl -p
Подробнее про эту проблему:
http://groups.google.com/group/redis-db/browse_thread/thread/dc4876861b174358
Background saving is failing with a fork() error under Linux even if I've a lot of free RAM!

