Loading...
Loading...
SQL tuning ended with the login query dropping from 900 milliseconds to 1 millisecond through a single sorted shortcut, plus covering indexes answering without touching the heap. Shortcuts help when the question has a column to descend on. A million users need their session, cart, and settings by identifier, in milliseconds, millions of times a minute. A relational database, which is a table store with a parser, planner, and transaction system for flexible questions, can answer give me the thing labeled X, but firing up all that machinery for an exact-key fetch is wasted work on the hottest path.
Caching the relational result without changing the access pattern fails next: each miss still pays planning and parsing, and the cache key design stays implicit instead of becoming the interface. A second tempting fix is a relational table with a primary-key lookup and nothing else, kept small and fully cached, and it fails on memory economics: 10 million sessions at 2 KB each need 20 GB before overhead, tripled to 60 GB with replicas, billed as database RAM with transaction machinery attached, while the same bytes in a key-value fleet serve 100 to 500 microseconds per read with no planner in the path. The lookup itself must get cheaper, not just its repetition.
A key-value store, which is a database mapping each exact key to one opaque value with no query language, skips the machinery: hand over the label and get back the blob. Think of a post office box wall where each numbered box holds whatever its owner left: one analogy for the whole idea, where the number is the key, the contents are the value, and nobody opens boxes to answer questions about all boxes.
One sentence version: hand over an exact key and get its exact value back in microseconds, with no joins, no parsing, and no questions beyond the key.
Namespaces live in the key text because the engine inspects nothing inside values. Prefixes such as user, session, and cart separate kinds, suffixes carry identifiers and versions, and the application owns the convention entirely. A typo in the prefix is a different key with no error, which is why key formats are documented and built by helpers rather than concatenated inline.
The prefixes are the design the store refuses to provide. Values stay opaque JSON, lists, or binary blobs that only the application interprets.
The interface is fetch, store, and delete by exact key, with expiry as the essential fourth behavior. Simplicity is the performance mechanism: hashing the key locates the value without planning, so lookups complete in microseconds and one connection serves thousands of operations per second.
GETOpen one numbered box and take its contents
GET user:123SETPut new contents into a numbered box
SET user:123 dataDELETEEmpty the box and discard its number
DEL user:123Expiry, set as time-to-live meaning seconds until the key deletes itself, bounds every session-like key. Without it, abandoned sessions accumulate until memory fills with boxes nobody opens.
Each workload reads one owner's blob by identifier and never asks across owners. Cached page renders avoid recomputation, login state shared across stateless servers makes horizontal scaling possible, carts and profiles arrive whole without joins, and feature flags serve thousands of reads per second with changes by hand.
Expensive computations stored under deterministic keys and reused instead of recomputed on every request.
Login state under a session key lets any server serve any request, which is what makes adding servers trivial.
One user's blob under one key arrives in a single fetch, where no relationship question was ever needed.
Switches read thousands of times per second and changed rarely, where microsecond reads dominate the value.
Redis, which is the in-memory store shipping lists, sets, sorted sets, streams, and messaging beyond plain strings, is the default because most teams use those structures within a year. Memcached, which is the multithreaded string-only cache with nothing else to operate, fits pure caching where simplicity beats features. DynamoDB, which is the managed key-value and document service billing per request with a 400 KB per-item ceiling, fits teams that prefer paying per operation over running servers, with large blobs kept in object storage instead.
In-memory speed plus lists, sets, sorted sets, streams, and messaging. A toolkit wearing a key-value interface, chosen when sessions, leaderboards, queues, and caches converge.
Strings in and strings out with multithreaded simplicity. Pick it when the job is purely caching and operational surface must stay minimal.
Key-value plus documents with no servers to feed, billed per request with single-digit-millisecond reads at any scale. Ideal when operations time matters more than per-request cost.
The speed mechanics are the absence of work: no parser, no planner, no joins, so sharding reduces to hashing the key to a machine. The cost mechanics are equally stark: questions like all users over 30 are unanswerable without a separate index, relationships and secondary lookups become application code, and fast memory bills by the gigabyte whether values are hot or forgotten.
Count the deployment: a warm key-value read answers in 100 to 500 microseconds, so one connection sustains thousands of operations per second and a small cluster clears millions. The budget that bites is memory, where 10 million sessions at 2 KB each need 10,000,000 times 2,000 bytes, which is 20,000,000,000 bytes or 20 GB before overhead, tripled to roughly 60 GB with replicas. Managed per-request billing flips the math to operations with single-digit-millisecond reads and a per-item ceiling that pushes large blobs to object storage. The knobs follow: expiry of 15 minutes to 24 hours on session-like keys so stale data deletes itself, least-recently-used eviction for caches with no-evict failure for primary stores, and kilobyte-scale values with blobs kept elsewhere so one giant value never blocks a connection.
| Knob | Sane value | Why that value holds |
|---|---|---|
| Expiry on sessions and rendered pages | 15 minutes to 24 hours | Bounds memory while stale data expires without a cleanup job |
| Eviction policy | Least-recently-used for caches, no-evict for primary stores | Caches shed cold keys while stores fail loudly instead of dropping writes |
| Value size | Kilobytes, with blobs in object storage | Large values block connections and multiply memory per key |
The homepage cache key expires and a thousand requests miss at once, all hitting the relational database simultaneously in a thundering herd, which is synchronized misses stampeding the backend the cache existed to shield. The defenses stack: jittered expiries so keys never die together, stale-while-revalidate, which serves the slightly old copy while one request rebuilds, and request coalescing, which makes duplicate in-flight misses share one backend fetch. The quieter killer is the hot key, where one celebrity profile requested 100,000 times a second hashes to one cluster node: replicate the value under several keys with random reads, cache it at the edge, or accept the single-node ceiling deliberately.
Exact keys answer in microseconds when the box number is known in advance, with jittered expiries and request coalescing holding back the thundering herd. Exact keys demand an exact label, though. Plenty of data arrives without a known number in hand and without a shared shape: products with seventeen optional fields, posts with nested comments, catalogs that change shape weekly. For those the value itself must carry a whole document rather than a pointer to one.