Раздел 37 · Redis с нуля

Персистентность: RDB и AOF

junior~20 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

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

Фотография и дневник

Представь, что ты ведёшь учёт книг на складе. Можно раз в час фотографировать все полки: фото быстро делается и быстро смотрится, но если склад сгорел через 50 минут после снимка, всё, что поменялось за эти 50 минут, пропало. А можно записывать в тетрадь каждое движение: «взял книгу 1», «положил книгу 7». Тетрадь пухнет, восстанавливать по ней долго, зато потеряешь в худшем случае последнюю строчку.

У Redis есть оба способа. Фотография называется RDB, дневник называется AOF. Свойство базы сохранять данные между перезапусками называют персистентностью.

Что будет без персистентности

Начнём с проверки. Три контейнера Redis 7: первый без сохранения на диск, второй с настройками по умолчанию, третий с включённым AOF. В каждый пишем один ключ:

docker run -d --name rp-none redis:7-alpine redis-server --save '' --appendonly no
docker run -d --name rp-rdb  redis:7-alpine
docker run -d --name rp-aof  redis:7-alpine redis-server --appendonly yes

docker exec rp-none redis-cli SET book:1:views 42
docker exec rp-rdb  redis-cli SET book:1:views 42
docker exec rp-aof  redis-cli SET book:1:views 42

Через пару секунд первый перезапускаем, а два других «роняем»: docker kill убивает процесс без предупреждения, как внезапное отключение питания.

docker restart rp-none
docker kill rp-rdb rp-aof && docker start rp-rdb rp-aof

Спрашиваем ключ у каждого:

rp-none: (nil)
rp-rdb:  (nil)
rp-aof:  "42"

Без персистентности ключ пропал даже при аккуратном перезапуске. С настройками по умолчанию он пропал при падении. Выжил только ключ в контейнере с AOF. Разберём почему.

RDB: снимок памяти

RDB это файл, в котором записано всё содержимое памяти на какой-то момент. Когда делать снимки, говорит настройка save:

127.0.0.1:6379> CONFIG GET save
1) "save"
2) "3600 1 300 100 60 10000"

Читается парами: «через 3600 секунд, если было хотя бы 1 изменение», «через 300 секунд, если изменений хотя бы 100», «через 60 секунд, если их хотя бы 10000». Наш контейнер rp-rdb упал через пару секунд после записи: ни одно условие не наступило, снимка не было, ключ потерян.

А при аккуратной остановке Redis сам делает последний снимок. Повторим с docker restart, который сначала просит процесс завершиться:

$ docker exec rp-rdb redis-cli SET book:1:views 42
OK
$ docker restart rp-rdb
$ docker exec rp-rdb redis-cli GET book:1:views
"42"

В журнале контейнера видно, как это произошло:

* Saving the final RDB snapshot before exiting.
* DB saved on disk
...
* Done loading RDB, keys loaded: 1, keys expired: 0.

Снимок можно сделать и вручную:

127.0.0.1:6379> SAVE
OK
127.0.0.1:6379> BGSAVE
Background saving started
127.0.0.1:6379> LASTSAVE
(integer) 1791621255

SAVE пишет снимок прямо сейчас и на это время останавливает все остальные команды: Redis однопоточный, мы помним это из первого урока. На большой базе это секунды простоя, поэтому на рабочем сервере SAVE не вызывают. BGSAVE делает то же в фоне: Redis создаёт копию своего процесса, копия пишет файл, а основной процесс продолжает отвечать клиентам. LASTSAVE возвращает время последнего удачного снимка в секундах от 1970 года.

Файл снимка лежит в каталоге из настройки dir, в официальном образе это /data:

$ docker exec rp-rdb ls -la /data
-rw-------    1 redis    redis          627 Oct 10 08:34 dump.rdb

У RDB два плюса: файл компактный, и Redis загружает его быстро. Его удобно копировать как резервную копию. Минус один, но серьёзный: всё, что изменилось после последнего снимка, при падении теряется.

урок из курса

Урок из курса «Redis с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.