28 March 2012

Redis in production

NoSQL
Хотелось бы рассказать о некоторых особенностях Redis при использовании на боевом сервере. Будут рассмотрены альтернативы при сохранении данных на диск, позволяющие достичь различной степени надёжности при сбоях. Так же будут приведены примеры конфигурации для резервного копирования и мониторинга. Используется Redis 2.2.11 на Amazon EC2 с установленной Ubuntu 10.10.



Резервное копирование



Мы используем 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!
Tags:redisbackupmonitlinux
Hubs: NoSQL
+31
55.1k 180
Comments 19
Top of the last 24 hours