Как организовать экспирацию ключей в Redis по расписанию

Как организовать экспирацию ключей в Redis по расписанию

Redis — одна из самых популярных in-memory баз данных, широко используемая для кэширования, хранения сессий, реализации очередей и управления распределёнными блокировками. Одной из ключевых особенностей Redis является возможность автоматического удаления данных по истечении срока действия (TTL — time to live). Однако в реальных сценариях часто возникает необходимость не просто устанавливать TTL на ключи, а организовать их экспирацию по расписанию — например, очищать определённые группы ключей в конкретное время суток, ежедневно или по расписанию cron.

Чтобы организовать экспирацию ключей в Redis по расписанию, используйте комбинацию команд EXPIREAT или PEXPIREAT с внешним планировщиком задач, таким как cron, systemd timer или специализированный сервис на Python/Node.js. Альтернативно — задействуйте Lua-скрипты или Redis Functions (начиная с версии 7.0), чтобы централизовать логику.

Зачем нужна запланированная экспирация ключей?

В большинстве случаев разработчики используют команду EXPIRE, чтобы указать, через сколько секунд ключ должен быть удалён. Это подходит для динамических данных, таких как токены доступа, временные кэши или данные сеансов. Но что, если вам нужно, чтобы все ключи категории «отчёты за день» удалялись ровно в 3:00 по МСК каждый день? Или чтобы статистика по пользователям обнулялась в начале каждого месяца?
Именно здесь стандартный TTL перестаёт быть достаточным. Вам нужен механизм, который позволяет запланировать удаление ключей на конкретную дату и время, независимо от момента их создания. Такой подход называется экспирацией по расписанию.
Представьте, что вы ведёте аналитику по посещаемости сайта. Вы сохраняете данные в Redis каждые 5 минут, но хотите, чтобы вся суточная статистика автоматически очищалась в 00:00. Простое использование EXPIRE key 86400 может привести к проблемам: если ключ создан в 23:59, он будет жить до 23:59 следующего дня, а не до 00:00. Это нарушает целостность временных границ.

Полезно знать: Redis не поддерживает встроенные cron-подобные триггеры. Любая расписанием управляемая экспирация требует внешнего или внутреннего оркестратора.

Также важно понимать, что сам Redis использует ленивую и активную очистку. Ключи с истёкшим TTL не удаляются мгновенно. Ленивая очистка происходит при попытке доступа к такому ключу, а активная — периодически, когда Redis проверяет случайные наборы ключей. Это значит, что даже после истечения времени ключ может физически оставаться в памяти.

Как работает TTL в Redis: основы

Прежде чем переходить к расписанию, необходимо чётко понимать, какие инструменты предоставляет Redis для управления временем жизни ключей. Основные команды:

  • EXPIRE key seconds — устанавливает срок жизни ключа в секундах с момента вызова.
  • EXPIREAT key timestamp — устанавливает абсолютное время истечения в формате Unix timestamp (в секундах).
  • PEXPIRE key milliseconds — аналогично EXPIRE, но в миллисекундах.
  • PEXPIREAT key millisecond-timestamp — абсолютное время в миллисекундах.
  • TTL key и PTTL key — показывают оставшееся время жизни.

Ключевой момент: именно EXPIREAT и PEXPIREAT позволяют планировать удаление на конкретное время, а не относительно текущего момента. Это делает их идеальными для работы с расписанием.
Например, чтобы установить, что ключ daily_report_2026-04-16 должен исчезнуть ровно в 03:00:00 по UTC (что соответствует 06:00 по МСК), вы можете выполнить:

  1. Вычислить timestamp: например, 16 апреля 2026 года в 03:00 UTC = 1776315600.
  2. Выполнить команду: EXPIREAT daily_report_2026-04-16 1776315600.

Теперь, независимо от того, когда был создан ключ, он будет помечен к удалению в указанное время.

«Используйте EXPIREAT вместо EXPIRE, когда требуется точное совпадение с календарным временем. Это особенно важно при работе с ночными ETL-процессами и регулярной очисткой.» — Алексей, DevOps-инженер, компания X

Однако Redis не «будит» себя в 03:00, чтобы удалить ключ. Он лишь помечает его как просроченный. Фактическая очистка зависит от стратегии garbage collection. С 6.0+ Redis улучшил алгоритм active expiry, проверяя до 20 ключей каждые 100 мс, но это всё ещё не гарантирует мгновенного удаления.

Установка времени истечения по расписанию: практические методы

Чтобы реализовать настоящую экспирацию по расписанию, нужно решить две задачи:

  1. Найти или определить ключи, которые должны быть удалены.
  2. Установить им время истечения с помощью EXPIREAT.

Для этого можно использовать шаблоны ключей, пространства имён (namespace) или хранить метаданные о группах ключей.
Рассмотрим пример. Допустим, вы используете префиксы для ключей с ежедневными данными:

  • stats:user:123:2026-04-16
  • stats:session:2026-04-16
  • cache:report:2026-04-16

Цель — установить всем ключам за сегодняшнюю дату TTL, чтобы они истекали в 00:00 следующих суток.
Шаги:

  1. Сформируйте шаблон поиска: stats:user:*:2026-04-16*.
  2. Используйте команду SCAN для безопасного перебора ключей (вместо KEYS, который блокирует сервер).
  3. Для каждого найденного ключа вычислите timestamp на 00:00 следующего дня.
  4. Вызовите EXPIREAT с этим значением.

Пример на Python с использованием библиотеки redis-py:

import redis
import time
from datetime import datetime, timedelta
r = redis.Redis(host='localhost', port=6379, db=0)
def schedule_expiration_for_date(target_date_str):
 # Преобразуем строку в объект даты
 target_date = datetime.strptime(target_date_str, "%Y-%m-%d")
 # Время истечения — 00:00 следующего дня
 expire_time = target_date + timedelta(days=1)
 expire_timestamp = int(expire_time.timestamp())
 # Шаблон поиска
 pattern = f"*:{target_date_str}"
 for key in r.scan_iter(match=pattern):
 r.expireat(key, expire_timestamp)
 print(f"Установлено EXPIREAT для {key.decode()} на {expire_timestamp}")
# Запуск для сегодняшней даты
schedule_expiration_for_date("2026-04-16")

Этот скрипт можно запускать ежедневно в 23:50, чтобы гарантировать, что все ключи за текущий день будут удалены в 00:00.

Полезно знать: Не используйте команду KEYS в production — она блокирует Redis на время выполнения. SCAN — безопасная альтернатива, хотя и медленнее.

Автоматизация через cron, systemd и скрипты

Чтобы процесс был полностью автоматическим, нужно интегрировать скрипт в систему планирования задач.
Наиболее распространённые варианты:

  • cron — классический Unix-демон для запуска задач по расписанию.
  • systemd timer — современная замена cron в Linux-системах.
  • внешний оркестратор — Kubernetes CronJob, Airflow, Celery Beat.

Пример записи в crontab для ежедневного запуска в 23:50:

50 23 * * * /usr/bin/python3 /opt/scripts/redis_schedule_expiration.py

Это обеспечит, что каждый вечер в 23:50 система установит EXPIREAT на все ключи за текущий день, и те будут удалены в 00:00.
Если вы используете Docker, можно поместить скрипт в отдельный контейнер и запускать его через docker exec или как sidecar-контейнер.
Альтернатива — использовать Node.js с библиотекой ioredis и планировщиком node-cron:

const Redis = require('ioredis');
const cron = require('node-cron');
const redis = new Redis();
cron.schedule('50 23 * * *', async () => {
 const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
 const tomorrow = new Date();
 tomorrow.setDate(tomorrow.getDate() + 1);
 tomorrow.setHours(0, 0, 0, 0);
 const expireTimestamp = Math.floor(tomorrow.getTime() / 1000);
 const stream = redis.scanStream({ match: `*:${today}*` });
 stream.on('data', (keys) => {
 if (keys.length) {
 keys.forEach(key => {
 redis.expireat(key, expireTimestamp);
 });
 }
 });
});
«Автоматизация через cron — простое и надёжное решение. Главное — протестировать скрипт в staging-среде и убедиться, что он не нагружает Redis в пиковые часы.» — Марина, SRE-инженер, FinTech-стартап

Redis Functions и Lua-скрипты: продвинутые подходы

Начиная с Redis 7.0, появилась мощная функциональность — Redis Functions. Это позволяет хранить и выполнять Lua-функции прямо внутри Redis, вызывая их по имени через FCALL.
Вы можете создать функцию, которая получает дату и устанавливает EXPIREAT на все ключи с этой датой:

# Файл: set_scheduled_expiry.lua
#!lua name=schedule_expiry
local function set_expiration_for_pattern(pattern, expire_timestamp)
 local cursor = 0
 repeat
 local result = redis.call('SCAN', cursor, 'MATCH', pattern, 'COUNT', 100)
 cursor = tonumber(result[1])
 local keys = result[2]
 for _, key in ipairs(keys) do
 redis.call('EXPIREAT', key, expire_timestamp)
 end
 until cursor == 0
end
redis.register_function('set_daily_expiration', function(keys, args)
 local date = args[1] -- например, "2026-04-16"
 local expire_ts = tonumber(args[2])
 set_expiration_for_pattern('*:' .. date, expire_ts)
 return "Scheduled expiration for " .. date
end)

Загрузка функции:

redis-cli -x FUNCTION LOAD REPLACE < set_scheduled_expiry.lua

Вызов:

redis-cli FCALL set_daily_expiration 0 2026-04-16 1776315600

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

  • Логика выполняется внутри Redis — меньше сетевых задержек.
  • Можно вызывать из любого клиента.
  • Поддержка транзакций и атомарности.

Также можно использовать Lua-скрипты без функций, отправляя их через EVAL или EVALSHA. Но Redis Functions — более современное и управляемое решение.

Типичные ошибки и как их избежать

  • Ошибка 1: Использование KEYS в production. Это может заблокировать Redis на несколько секунд при большом объёме данных. Всегда используйте SCAN или SSCAN для итерации.
  • Ошибка 2: Неправильный расчёт timestamp. Убедитесь, что вы используете UTC, а не локальное время. Разница между часовыми поясами может привести к сбою расписания.
  • Ошибка 3: Запуск скрипта в пиковое время. Если ваша система активна в 23:50, добавление нагрузки от сканирования ключей может ухудшить производительность. Выберите менее нагруженное окно.
  • Ошибка 4: Отсутствие логирования и мониторинга. Не забывайте логировать запуск скриптов и проверять, сколько ключей было обработано. Используйте Prometheus + Redis Exporter для контроля.
  • Ошибка 5: Игнорирование механизма active expiry. Даже после установки EXPIREAT ключ может остаться в памяти. Настройте параметры active-expire-effort (от 1 до 10) в redis.conf для более агрессивной очистки.
Ошибка
Последствия
Решение
Использование KEYS *
Блокировка Redis, latency spikes
Перейти на SCAN с COUNT
Локальное время вместо UTC
Сдвиг времени экспирации
Всегда использовать UTC в timestamp
Отсутствие резервного копирования
Потеря данных при сбое
Настроить RDB/AOF
Неоптимальный интервал cron
Переэкспирация или недостаточная очистка
Тестировать и настраивать под рабочую нагрузку

Рекомендации и лучшие практики

  • Используйте осмысленные префиксы. Например, session:, report:, temp:. Это упрощает поиск и массовое управление.
  • Храните метаданные в отдельном ключе. Например, meta:expiration:schedule — хэш с датами и временем очистки.
  • Тестируйте на staging. Перед внедрением в продакшен проверьте поведение скрипта на реплике.
  • Настройте active expiry effort. В redis.conf: active-expire-effort 4 — хороший баланс между нагрузкой и эффективностью.
  • Рассмотрите использование RedisTimeSeries или RedisJSON. Если данные структурированы, можно применять более сложные стратегии очистки по полям.
«Если вы регулярно очищаете одни и те же типы ключей — вынесите логику в отдельный микросервис. Это упростит масштабирование и мониторинг.» — Дмитрий, архитектор высоконагруженных систем

Заключение

Организация экспирации ключей в Redis по расписанию — это не встроенная функция, а архитектурное решение, сочетающее знание Redis API, планировщиков задач и лучших практик управления состоянием. Ключевые элементы успеха: использование EXPIREAT для абсолютного времени, безопасный перебор ключей через SCAN, автоматизация через cron или оркестраторы и контроль через мониторинг.

Главное — не полагаться на ленивую очистку Redis. Планируйте экспирацию заранее, используйте правильные инструменты и тестируйте в условиях, близких к боевым.
  • Для точного времени удаления используйте EXPIREAT, а не EXPIRE.
  • Никогда не используйте KEYS в production — только SCAN.
  • Автоматизируйте процесс через cron, systemd или оркестраторы.
  • Redis Functions (версия 7.0+) позволяют хранить логику внутри Redis.
  • Настройте active expiry effort и контролируйте нагрузку на сервер.
⚠️ Дисклеймер — нажмите, чтобы развернуть

Материалы, опубликованные в разделе «Блог» на сайте RU DESIGN SHOP (rudesignshop.ru), носят исключительно информационный и ознакомительный характер и не являются руководством к действию, финансовой рекомендацией, медицинской услугой, ветеринарным назначением либо рекламой товаров и услуг, включая азартные игры. Публикации не содержат призывов к участию в азартных играх и не направлены на продвижение соответствующих операторов.

Безопасность применения товаров и веществ: при использовании строительных материалов, бытовой химии, пестицидов и агрохимикатов необходимо строго следовать инструкциям производителя и действующему законодательству Российской Федерации, включая Федеральный закон РФ от 19.07.1997 № 109-ФЗ «О безопасном обращении с пестицидами и агрохимикатами».

Упоминание товарных знаков, брендов и организаций носит исключительно информационный характер и не означает наличие партнёрских отношений или одобрения со стороны правообладателей.

Материалы, содержащие сведения о медицинских, ветеринарных или косметических средствах, представлены в справочных целях и не являются медицинской консультацией или назначением. Перед применением рекомендуется обратиться к врачу, ветеринарному специалисту или иному сертифицированному профессионалу.

Возрастные ограничения: материалы, содержащие сведения о продукции категории 18+, включая алкоголь или азартные игры, предназначены исключительно для совершеннолетней аудитории и публикуются в информационных целях.

Правовая ответственность: решения, принятые на основе опубликованной информации, пользователь принимает самостоятельно и на свой риск; редакция и авторы несут ответственность в пределах, установленных законодательством Российской Федерации.

Редакция не допускает публикаций, содержащих пропаганду экстремизма, терроризма, наркотических средств или суицида; подобные материалы подлежат немедленному удалению.

Упоминание организаций с ограниченным статусом: компания Meta Platforms Inc. (социальные сети Facebook и Instagram) признана экстремистской организацией решением суда РФ, её деятельность запрещена на территории Российской Федерации; любые упоминания приводятся исключительно в информационных целях.

Авторские права и источники: информация собирается из открытых источников; её актуальность указывается на дату публикации и может изменяться.

Изображения и иллюстрации используются на условиях, разрешённых правообладателями. При возникновении претензий редакция готова оперативно рассмотреть обращение и внести необходимые изменения.

Персональные данные и cookies: сайт использует cookies и обрабатывает персональные данные пользователей в соответствии с Федеральным законом № 152-ФЗ «О персональных данных» и Политикой конфиденциальности RU DESIGN SHOP.

Мнения авторов могут не совпадать с позицией государственных органов или коммерческих организаций, упомянутых в материалах.

 

РЕКОМЕНДУЕМ
Товары от российских производителей