- PVSM.RU - https://www.pvsm.ru -

Redis in production

Хотелось бы рассказать о некоторых особенностях Redis [1] при использовании на боевом сервере. Будут рассмотрены альтернативы при сохранении данных на диск, позволяющие достичь различной степени надёжности при сбоях. Так же будут приведены примеры конфигурации для резервного копирования и мониторинга. Используется 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 [2]
http://antirez.com/post/redis-persistence-demystified.html [3]

Для бэкапов мы используем замечательный backup gem [4], в котором есть поддержка 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 [5], который настроен уведомлять о всех происходящих событиях. Настройки для него достаточно просты:

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 [6], которая делает форк текущего процесса и в этом форке данные записываются на диск. Таким образом, основной поток не блокируется и запись происходит асинхронно. Проблема в том, что в UNIX-системах при вызове fork(), в дочерний процесс так же копируется содержимое памяти, которую использует родительский процесс. Допустим, если Redis в текущий момент времени занимает 2Gb памяти, а в системе остался только 1Gb свободной памяти, то при выполнении команды BGSAVE возможна следующая ошибка:

[18696] 28 Mar 12:26:54 # Can't save in background: fork: Cannot allocate memory

В современных системах при копировании памяти для форков используется метод Copy on Write [7]. Память копируется только тогда, когда происходит запись в соответствующий участок. 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 [8]

Автор: akhkharu


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/linux/4397

Ссылки в тексте:

[1] Redis: http://redis.io/

[2] http://redis.io/topics/persistence: http://redis.io/topics/persistence

[3] http://antirez.com/post/redis-persistence-demystified.html: http://antirez.com/post/redis-persistence-demystified.html

[4] backup gem: https://github.com/meskyanichi/backup

[5] monit: http://mmonit.com/monit/

[6] BGSAVE: http://redis.io/commands/bgsave

[7] Copy on Write: http://en.wikipedia.org/wiki/Copy-on-write

[8] http://groups.google.com/group/redis-db/browse_thread/thread/dc4876861b174358: http://groups.google.com/group/redis-db/browse_thread/thread/dc4876861b174358