В прошлом уроке мы сложили события заказов в стрим и назвали его журналом, который хранит историю. Но весь этот журнал лежит в оперативной памяти. Выдерни шнур из сервера, и память очистится. Сегодня разбираемся, как 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 ₽.
Оплата пока не подключена: премиум и курсы открываются промокодом.