ключ — значение — где это незаменимоkey-value — where it's indispensable
Главная ценность key-value хранилищ — субмиллисекундный отклик за счёт работы полностью в оперативной памяти. Это определяет три сценария, где им нет разумной альтернативы: кэш, сессии с TTL, распределённые блокировки.The core value of key-value stores is sub-millisecond latency from living entirely in RAM. That drives three scenarios where there's no reasonable alternative: caching, TTL-based sessions, distributed locks.
Запрос, требующий JOIN нескольких таблиц или агрегации по миллионам строк, может занимать десятки-сотни миллисекунд в реляционной БД — при высокой частоте одинаковых запросов (топ товаров, профиль пользователя, курс валют) это прямой кандидат на кэш перед базой. Key-value хранилище отдаёт готовый результат за микросекунды, снимая нагрузку с БД и сокращая задержку на порядки.A query requiring a JOIN across several tables or an aggregation over millions of rows can take tens to hundreds of milliseconds in a relational DB — under high frequency of identical requests (top products, a user profile, an exchange rate), that's a direct candidate for a cache in front of the database. A key-value store returns the ready result in microseconds, taking load off the DB and cutting latency by orders of magnitude.
То же самое применимо к ответам внешних API: если сторонний сервис отвечает медленно, ограничен по частоте запросов (rate limit), или сам ответ не меняется чаще, чем раз в несколько минут, — кэшировать его на стороне своего сервиса дешевле и надёжнее, чем бить по внешнему API на каждый запрос пользователя.The same applies to external API responses: if a third-party service responds slowly, is rate-limited, or the response itself doesn't change more often than every few minutes — caching it on your own service's side is cheaper and more reliable than hitting the external API on every user request.
Сессия по своей природе — временные данные с чётким сроком жизни: пользователь залогинился, сессия живёт до логаута или истечения таймаута бездействия. Key-value хранилища почти всегда поддерживают TTL (Time-To-Live) на уровне самого движка — установил ключ с временем жизни, и он исчезает сам, без отдельного фонового процесса очистки, который пришлось бы писать вручную поверх реляционной БД.A session is by nature temporary data with a clear lifespan: a user logs in, the session lives until logout or an inactivity timeout. Key-value stores almost always support TTL (Time-To-Live) at the engine level itself — set a key with a lifetime, and it disappears on its own, with no separate background cleanup process that would otherwise need to be written by hand on top of a relational DB.
Для JWT это особенно важно в сценарии отзыва токена (revocation): сам JWT — самодостаточная подписанная структура, которую нельзя "отозвать" на лету без централизованного состояния — типичное решение — держать blacklist отозванных токенов в key-value хранилище с TTL, равным оставшемуся сроку жизни токена, и не спрашивать по нему после этого срока вообще.For JWTs this matters especially for token revocation: a JWT is a self-contained signed structure that can't be "revoked" on the fly without centralized state — the typical fix is keeping a blacklist of revoked tokens in a key-value store with TTL equal to the token's remaining lifetime, and simply not checking it after that point.
Когда несколько инстансов одного сервиса (или несколько разных сервисов) должны гарантированно выполнить операцию не более одного раза одновременно (например, отправить письмо-напоминание ровно один раз, хотя таймер сработал на всех репликах), нужен механизм блокировки, работающий не в рамках одного процесса, а через сеть.When multiple instances of one service (or several different services) must guarantee an operation runs at most once concurrently (say, sending a reminder email exactly once even though a timer fired on every replica), a locking mechanism working not within one process but across the network is needed.
Атомарная операция "установить ключ, только если он ещё не существует" (`SET key value NX EX ttl` в Redis) — готовый строительный блок для такой блокировки: первый процесс, успешно выполнивший `SETNX`, становится владельцем блокировки, остальные получают отказ и либо ждут, либо пропускают операцию. TTL на самой блокировке защищает от вечного зависания, если владелец упал, не успев её снять.The atomic "set a key only if it doesn't already exist" operation (`SET key value NX EX ttl` in Redis) is a ready-made building block for such a lock: the first process to successfully run `SETNX` becomes the lock's owner, the rest get a failure and either wait or skip the operation. A TTL on the lock itself guards against it hanging forever if the owner crashes before releasing it.
Если процесс-владелец блокировки "подвис" (например, долгая пауза GC) дольше TTL, лок истекает, второй процесс успешно захватывает его, а затем "подвисший" первый процесс просыпается и продолжает работу, думая, что всё ещё владеет блокировкой — оба процесса теперь работают параллельно. Правильное решение сложнее одного `SETNX` — например, алгоритм Redlock (с оговорками относительно его гарантий) или fencing tokens.If the lock-owning process "hangs" (say, a long GC pause) longer than the TTL, the lock expires, a second process successfully acquires it, and then the "hung" first process wakes up and continues, believing it still owns the lock — both processes are now running concurrently. The correct fix is more involved than a single `SETNX` — e.g., the Redlock algorithm (with caveats about its actual guarantees) or fencing tokens.
Кэшированное значение, устаревшее без TTL и без явной инвалидации при изменении исходных данных, — классический источник "показывает старую цену" багов. TTL — простое, но грубое решение (данные могут быть устаревшими вплоть до истечения); явная инвалидация при записи в основную БД — точнее, но требует дисциплины поддерживать её во всех местах записи.A cached value that goes stale with no TTL and no explicit invalidation on the underlying data changing is a classic source of "shows the old price" bugs. TTL is a simple but blunt fix (data can be stale right up until expiry); explicit invalidation on writes to the primary DB is more precise, but requires the discipline to maintain it at every write site.
Если TTL записи в blacklist не привязан к оставшемуся сроку жизни самого JWT (а, например, поставлен фиксированным или не поставлен вовсе), blacklist будет либо расти бесконечно, либо забывать об отозванных токенах раньше, чем они естественным образом истекут — оба варианта — ошибка проектирования, а не мелочь.If the blacklist entry's TTL isn't tied to the JWT's own remaining lifetime (say, it's fixed or not set at all), the blacklist either grows unbounded or forgets about revoked tokens before they'd naturally expire — either case is a design error, not a minor detail.