Кэширование в Redis с Двойным Ключом. Начало
Кэширование в Redis — это просто… пока вы не осознаете, что вашей сущности нужны два разных ключа поиска: один внутренний и один внешний. Тут всё становится сложнее: если не быть внимательным, приложение может стать жертвой устаревших данных, пропущенных инвалидаций и несогласованного состояния между сервисами.
Проблема
Представьте, что вы разрабатываете типичное приложение. У каждого пользователя есть:
- UserId (GUID, внутренний, неизменяемый)
- Email (используется для входа, внешний, изменяемый)
Теперь рассмотрим, как система взаимодействует с этим профилем пользователя.
Вариант 1. Аутентификация (Email → User)
При входе в систему служба идентификации должна:
1. Найти пользователя по email;
2. Загрузить его учётные данные;
3. Загрузить его профиль (роли, настройки и т.п.)
Для этого требуется быстрый поиск по email. Выполнение этого SQL-запроса в часы пиковых нагрузок может замедлить систему. Redis решает эту проблему.
Но остальная бизнес-логика ведёт себя по-другому…
Вариант 2. Внутренние микросервисы (UserId → Профиль)
Биллинг, уведомления, аналитика и журналы аудита — все они идентифицируют пользователей по внутреннему UserId. Поэтому они ожидают быстрого поиска по UserId.
Если вы кэшируете только по одному ключу:
- UserId - трафик аутентификаций приводит к задержкам,
- Email - внутренние сервисы постоянно используют БД.
Кэширование с двойным ключом
Кэширование с двойным ключом позволяет получить доступ к одной и той же сущности, используя два разных ключа:
- По внутреннему, стабильному ключу (UserId),
- По внешнему, доступному пользователю, ключу (Email).
Но есть и более серьёзная проблема…
Наивный разработчик может сказать: «Просто храните полный JSON объекта под обоими ключами!»
Это сработает, на время. Но,
- если пользователь сменит email, нужно удалить запись со старым ключом;
- приходится удалять два элемента каждый раз при изменении пользовательских данных;
- сбои в работе сети между двумя вызовами StringSetAsync = повреждение кэша;
- вы тратите память, храня дублирующиеся JSON-объекты.
Поэтому профессиональные системы используют другой подход.
Единый источник данных + ключ индекса
Храним полный профиль пользователя ОДИН РАЗ:
user : data : {userId} → JSONХраним индекс Email → UserId:
user : email : {email} → userIdЭто обеспечивает:
- отсутствие дублирования JSON-данных,
- отсутствие несоответствий между первичными и вторичными ключами,
- безопасные обновления email,
- простую инвалидацию,
- производительный поиск в обоих случаях.
Далее посмотрим, как это реализовать в .NET.
Окончание следует…
Источник: https://thecodeman.net/posts/dual-key-redis-caching-in-dotnet