TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #3009 2.27K
День 2506. #ЗаметкиНаПолях
Кэширование в Redis с Двойным Ключом. Окончание

Начало

Реализация Двойного Ключа в .NET
1. DTO + Хелпер для ключей Redis
public class UserDto
{
public Guid UserId { get; set; }
public string Email { get; set; }
public string DisplayName { get; set; }
public string TimeZone { get; set; }
}

public static class UserKeys
{
public static string Data(Guid id)
=> $"user:data:{id}";

public static string Email(string email)
=> $"user:email:{email.ToLowerInvariant()}";
}


2. Кэширование пользователя (атомарная запись)
public async Task CacheUserAsync(UserDto user)
{
var dataKey = UserKeys.Data(user.UserId);
var emailKey = UserKeys.Email(user.Email);
var json = JsonSerializer.Serialize(user);
var tran = _db.CreateTransaction();

tran.StringSetAsync(dataKey, json, TimeSpan.FromMinutes(10));
tran.StringSetAsync(emailKey, user.UserId.ToString(), TimeSpan.FromMinutes(10));

await tran.ExecuteAsync();
}


- оба ключа обновляются совместно;
- исключён риск частичных записей;
- JSON хранится 1 раз.

3. Поиск по UserId
public async Task<UserDto?> 
GetByIdAsync(Guid userId)
{
var json = await _db.StringGetAsync(
UserKeys.Data(userId));

return json.IsNullOrEmpty
? null
: JsonSerializer.Deserialize<UserDto>(json);
}


4. Поиск по Email
public async Task<UserDto?>
GetByEmailAsync(string email)
{
var id = await _db.StringGetAsync(UserKeys.Email(email));
if (string.IsNullOrEmpty(id)) return null;

var userId = Guid.Parse(id);
return await GetByIdAsync(userId);
}


5. Безопасная обработка изменений email
public async Task UpdateEmailAsync(
Guid userId,
string oldEmail,
string newEmail)
{
var oldKey = UserKeys.Email(oldEmail);
var newKey = UserKeys.Email(newEmail);

var tran = _db.CreateTransaction();

// ... обновляем данные пользователя

tran.KeyDeleteAsync(oldKey);
tran.StringSetAsync(newKey, userId.ToString());

await tran.ExecuteAsync();
}

- старый индекс удаляется, добавляется новый;
- ключ данных не изменяется;
- нет дублирования JSON;
- нет несогласованного состояния кэша.

Если вы не будете использовать кэширование с двойным ключом, то рано или поздно, вы столкнетесь с…
- устаревшими данными в кэше;
- неработающим входом в систему (изменён email, но кэш не обновился);
- внутренними микросервисами, возвращающими устаревшие значения;
- «фантомными пользователями» в логах;
- и т.п.
Большинство этих ошибок никогда не проявятся в процессе разработки — только в рабочей среде под реальной нагрузкой.

Шаблон кэширования с двойным ключом универсален для современных систем:
1. Электронная коммерция
- ProductId → данные
- Артикул → ProductId

2. CMS
- ContentId → данные
- Slug → ContentId

3. IoT
- DeviceId → данные
- MAC-адрес/Серийный номер → DeviceId

Итого
Кэширование Redis с двойным ключом — не оптимизация, а фундаментальная архитектура для современных систем .NET, использующих Redis. Его следует использовать, когда:
- сущность имеет несколько идентификаторов;
- один или несколько из этих идентификаторов изменяемы;
- необходим быстрый поиск из разных контекстов;
- нужно избежать дублирования JSON в Redis;
- важна согласованность кэша под нагрузкой.

Правильный шаблон:
- один канонический ключ данных;
- несколько легковесных ключей индекса;
- атомарные обновления для обеспечения согласованности.
Так вы избежите почти всех проблем с несогласованностью кэша еще до их появления.

Источник: https://thecodeman.net/posts/dual-key-redis-caching-in-dotnet
  • 👍 14
More from @netdeveloperdiary
  1. Sep 29, 2026Фото 3 (с) Анатолий Кулаков
  2. Sep 29, 2026День 2799. Конференция DotNext 2026. Часть 1 25 и 26 сентября в Москве прошла очередная ко…
  3. Sep 28, 2026День 2798. #Оффтоп Утиная Типизация в C# с Помощью Перехватчиков. Часть 2 Некоторое время…
  4. Sep 27, 2026День 2797. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Окончание Начало Продол…
  5. Sep 26, 2026День 2796. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Продолжение Начало Три…
  6. Sep 25, 2026День 2795. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Начало Проблема с позиц…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →