← Назад к вопросам

Что такое снимок (snapshot) в Redis?

2.0 Middle🔥 171 комментариев
#DevOps и инфраструктура#Django

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Снимок (Snapshot) в Redis

Snapshot (RDB — Redis Database) — это механизм сохранения полного состояния базы данных Redis на диск в определённый момент времени. Это бинарный файл, который содержит весь набор ключей и значений.

Основная концепция

Redis хранит данные в оперативной памяти, что делает его очень быстрым, но уязвимым к потере данных при отключении. Snapshot позволяет периодически сохранять состояние БД на диск:

# Пример работы с Redis и снимками
from redis import Redis

redis_client = Redis(host="localhost", port=6379)

# Установка значений
redis_client.set("user:1:name", "Alice")
redis_client.set("user:1:email", "alice@example.com")
redis_client.set("counter", 100)

# Создание снимка (выполняется на сервере)
redis_client.bgsave()  # Background Save
print("Снимок создан")

# Все данные сохранены на диск в файл dump.rdb

Типы снимков

1. SAVE — синхронное сохранение

redis_client.save()  # Блокирует сервер до завершения

# Во время SAVE:
# - Все клиенты ждут
# - Запросы не обрабатываются
# - Данные записываются на диск

2. BGSAVE — асинхронное сохранение

redis_client.bgsave()  # Background Save

# Во время BGSAVE:
# - Создаётся дочерний процесс (fork)
# - Основной процесс продолжает обрабатывать запросы
# - Дочерний процесс пишет на диск
# - Клиенты не блокируются

Конфигурация снимков

# В файле redis.conf:
"""
# Сохранять снимок при условиях:
# save <seconds> <changes>
# - Каждые 15 минут, если было 1 или больше изменений
save 900 1

# Каждые 5 минут, если было 10 или больше изменений
save 300 10

# Каждую минуту, если было 10000 или больше изменений
save 60 10000

# Отключить автосохранение
save ""

# Файл для снимков
dbfilename dump.rdb

# Директория для сохранения
dir ./
"""

Файловая структура снимка

# RDB файл содержит:
"""
dump.rdb:
├── Версия RDB
├── Метаинформация (версия Redis, время создания)
├── Выбранная БД (обычно DB 0)
├── Все ключи и значения
│   ├── Тип данных
│   ├── Ключ
│   ├── Значение
│   ├── TTL (время жизни)
├── Контрольная сумма (CRC64)
└── EOF маркер
"""

Процесс создания снимка

import time
from redis import Redis

redis = Redis()

# Шаг 1: Установка данных
for i in range(1000):
    redis.set(f"key:{i}", f"value_{i}")
    redis.expire(f"key:{i}", 3600)  # 1 час

print(f"Размер БД: {redis.dbsize()} ключей")

# Шаг 2: Запуск BGSAVE
start_time = time.time()
redis.bgsave()
print("BGSAVE запущен")

# Шаг 3: Проверка статуса
while True:
    info = redis.info('persistence')
    if info['rdb_bgsave_in_progress']:
        elapsed = time.time() - start_time
        print(f"Сохранение в прогрессе... ({elapsed:.1f}s)")
        time.sleep(1)
    else:
        print(f"Сохранение завершено за {time.time() - start_time:.1f}s")
        break

# Шаг 4: Проверка файла
last_save = info['rdb_last_save_time']
changes_since = info['rdb_changes_since_last_save']
print(f"Последний снимок: {last_save}")
print(f"Изменений с последнего снимка: {changes_since}")

Восстановление данных из снимка

# Redis автоматически восстанавливает данные при запуске

# 1. При перезагрузке Redis:
# - Читает dump.rdb из диска
# - Восстанавливает все ключи и значения в памяти
# - Сервер готов к работе

import subprocess
import time

# Остановка Redis
subprocess.run(["redis-cli", "SHUTDOWN"])
print("Redis остановлен")

time.sleep(2)

# Запуск Redis (восстановление из dump.rdb)
subprocess.Popen(["redis-server"])
print("Redis запущен, восстановление из снимка...")

time.sleep(2)

# Проверка восстановления
from redis import Redis
redis = Redis()
value = redis.get("key:0")
print(f"Восстановленное значение: {value}")  # value_0

Преимущества и недостатки снимков

Преимущества:

  • Компактное представление (бинарный формат)
  • Быстрое восстановление
  • Низкий overhead во время создания (BGSAVE)
  • Полный снимок состояния

Недостатки:

  • Потеря данных между снимками
  • Использует много памяти (fork создаёт копию)
  • Может занимать длительное время на больших БД
  • Нет гарантии последовательности

Альтернатива: AOF (Append-Only File)

# В redis.conf:
"""
# Включить AOF
appendonly yes

# Имя файла
appendfilename "appendonly.aof"

# Синхронизация на диск:
# always — после каждой операции (медленно, надёжно)
# everysec — раз в секунду (по умолчанию)
# no — зависит от OS
appendfsync everysec
"""

# AOF содержит все команды:
"""
appendonly.aof:
*3\r\n
SET\r\n
key:1\r\n
value_1\r\n

*3\r\n
SET\r\n
key:2\r\n
value_2\r\n

*2\r\n
DEL\r\n
key:1\r\n
"""

Гибридный подход

# Использование обоих механизмов одновременно

# redis.conf:
"""
# RDB снимки каждые 15 минут
save 900 1

# AOF для дополнительной надёжности
appendonly yes
appendfsync everysec
"""

# Это обеспечивает:
# - Быстрое восстановление (RDB)
# - Минимальную потерю данных (AOF)

Практический пример

from redis import Redis
import time
import signal
import sys

class RedisPersistence:
    def __init__(self):
        self.redis = Redis(host="localhost", port=6379)
    
    def create_snapshot(self):
        """Создать снимок"""
        print("Создание снимка...")
        self.redis.bgsave()
        
        while True:
            info = self.redis.info('persistence')
            if info['rdb_bgsave_in_progress']:
                print(".", end="", flush=True)
                time.sleep(0.5)
            else:
                print("\nСнимок создан!")
                break
    
    def check_snapshot_info(self):
        """Получить информацию о снимке"""
        info = self.redis.info('persistence')
        return {
            "last_save_time": info['rdb_last_save_time'],
            "changes_since_save": info['rdb_changes_since_last_save'],
            "file_size": info.get('rdb_current_bgsave_time', 'N/A')
        }
    
    def estimate_memory(self):
        """估ить использование памяти"""
        info = self.redis.info('memory')
        used_mb = info['used_memory'] / 1024 / 1024
        peak_mb = info['used_memory_peak'] / 1024 / 1024
        return {
            "used_mb": used_mb,
            "peak_mb": peak_mb
        }

persist = RedisPersistence()

# Заполнение данных
for i in range(100000):
    persist.redis.set(f"data:{i}", f"value_{i}" * 10)

# Создание снимка
persist.create_snapshot()

# Информация
info = persist.check_snapshot_info()
print(f"Информация о снимке: {info}")

memory = persist.estimate_memory()
print(f"Использование памяти: {memory['used_mb']:.1f}MB")

Синхронизация снимков в кластере

# Redis Sentinel следит за снимками
# и синхронизирует их между мастером и репликами

# sentinel.conf:
"""
sentinel monitor mymaster 127.0.0.1 6379 1
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
"""

# При failover снимок синхронизируется автоматически

Снимки в Redis — критический механизм для обеспечения durability и восстановления после сбоев.

Что такое снимок (snapshot) в Redis? | PrepBro