Contents
Course · 05
How does Redis stay so fast, and when can it lose your data?
Redis is a data store that lives in memory. This page shows how it works inside, how it saves to disk, how it scales, and the traps interviewers love. Every fact is checked against redis.io and other official docs on 2026-10-25.
Redis is fast because it keeps all data in RAM and runs every command on one main thread, so there are no locks and no disk reads.
- It is not just a cache: it has lists, hashes, sets, sorted sets, streams, bitmaps, HyperLogLog and geo indexes.
- Durability is a choice: RDB snapshots can lose minutes; AOF with
appendfsync everyseccan lose about 1 second. - Replication is asynchronous, so Sentinel and Cluster failovers can lose acknowledged writes.
- Cluster splits keys into 16,384 hash slots; multi-key commands only work when all keys share a slot.
- Locks with
SET NX PXare fine for efficiency; for correctness you also need fencing tokens.
everysec—Words you need
Core ideas
01What Redis is, and why it is fast
Redis means REmote DIctionary Server. It is a key-value store. The value can be a rich data structure, not just a string. All data sits in RAM. Disk is used only to save copies.
Three things make it fast. First, reads never touch disk. Second, one main thread runs every command, so no locks are needed. Third, an event loop with epoll (Linux) lets that one thread watch thousands of sockets. Multiplexing means one thread handles many connections by only working on the ready ones.
redis.conf). The FAQ says Redis is usually memory- or network-bound, not CPU-bound, and can reach 1 million requests per second with pipelining.Single-threaded means one slow command blocks everyone. That is the price of having no locks.
Source: redis.io latency doc · redis.io FAQ · Redis 6.0 release notes · event library (ae.c)
02Data structures and what each is for
Redis calls itself a "data structure server". Each type has its own commands, and each command is atomic. Atomic means it runs fully or not at all, with nothing in between.
| Type | What it is | Real use | Key commands |
|---|---|---|---|
| String | Bytes, up to 512 MB | Cache a page, counters, flags | SET GET INCR SET NX PX |
| List | Linked list, fast at both ends | Job queue, latest-N feed | LPUSH BRPOP LMOVE LTRIM |
| Hash | Field → value map | User profile, session | HSET HGET HINCRBY |
| Set | Unique, unordered members; O(1) add and check | Tags, who liked a post | SADD SISMEMBER SINTER |
| Sorted set | Unique members ordered by score (skip list + hash table) | Leaderboard, time-ordered index | ZADD ZINCRBY ZREVRANGE ZREVRANK |
| Bitmap | Bit operations on a string | Daily active users by user id | SETBIT BITCOUNT BITOP |
| HyperLogLog | Approximate count of unique items | Unique visitors per page per day | PFADD PFCOUNT PFMERGE |
| Geospatial | Points stored in a sorted set as geohash scores | Find bikes within 5 km | GEOADD GEOSEARCH |
| Stream | Append-only log with consumer groups | Event processing, reliable queue | XADD XREADGROUP XACK |
ZADD game:scores 4200 "ana" 3100 "raj" # O(log N) per member
ZINCRBY game:scores 50 "raj" # add points
ZREVRANGE game:scores 0 9 WITHSCORES # top 10
ZREVRANK game:scores "raj" # my rank, O(log N)
ZREM. Small hashes, sets and sorted sets use a compact listpack encoding (for example hash-max-listpack-entries 512), using up to 10 times less memory.In an interview, name the data structure first. "Sorted set" answers leaderboard, ranking and time-window questions.
Source: data types · sorted sets · HyperLogLog · bitmaps · GEOADD · memory optimization
03TTL and how keys really expire
You set a TTL with EXPIRE key 60 or SET key v EX 60. Writing a new value with SET clears the TTL. INCR, LPUSH and HSET keep it.
| Way | When it runs | What it does |
|---|---|---|
| Lazy (passive) | When a client touches the key | If the key is past its time, delete it and answer "not found" |
| Active | Every 100 ms (10 times a second) | Sample 20 keys that have a TTL, delete the expired ones; if more than 25% were expired, repeat |
ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP = 20 keys, 10 times per second, and repeats while more than 25% are expired. So if many keys expire in the same second, Redis can block for a while. Expiry error is 0 to 1 ms since 2.6. Replicas do not expire keys themselves: the primary sends a DEL to replicas and to the AOF. TTLs are stored as absolute Unix times, so a big clock jump can expire everything at once.Add random jitter to TTLs. Many keys expiring in the same second cause latency spikes and cache stampedes.
Source: EXPIRE · latency doc, "Latency generated by expires"
04Eviction when memory is full
Set a limit with maxmemory 2gb. When a write would go over it, Redis follows the policy. The default policy is noeviction: writes get an error, reads still work.
| Policy | Which keys can go | Pick it when |
|---|---|---|
noeviction | None; writes fail | Redis is a database, losing keys is worse than errors |
allkeys-lru | Any key, least recently used | Pure cache; a good default per the docs |
allkeys-lfu | Any key, least frequently used | Some keys are always hot, others one-off |
allkeys-random | Any key, random | All keys are used about equally |
volatile-lru / -lfu / -random | Only keys with a TTL | Cache keys and permanent keys share one instance |
volatile-ttl | Keys with the shortest TTL left | You mark throw-away keys with short TTLs |
allkeys-lrm / volatile-lrm | Least recently modified (Redis 8.6+) | Read-heavy data that is rarely updated |
maxmemory-samples 5) and evicts the oldest, with a pool of good candidates since 3.0. True LRU would cost more memory. LFU (since 4.0) uses a Morris counter of a few bits that decays every minute and saturates near one million hits. volatile-* policies behave like noeviction if no key has a TTL. Replication and AOF buffers are not counted in maxmemory, so leave some RAM spare.Cache: allkeys-lru or allkeys-lfu. Primary data: noeviction and alerts on memory.
Source: Key eviction · redis.conf (default noeviction)
Keeping data safe
05Persistence: RDB vs AOF vs both
| Option | How | Can lose | Good for |
|---|---|---|---|
| RDB | fork() a child that writes a compact snapshot; e.g. save 60 1000 | Minutes since last snapshot | Backups, fast restarts |
AOF always | fsync on every write batch | Almost nothing; "very very slow" | Rare, strict cases |
AOF everysec (default) | fsync once a second in a background thread | About 1 second | Most systems |
AOF no | OS decides (Linux about every 30 s) | Up to ~30 s | Speed over safety |
| RDB + AOF | Both on; AOF is used at restart | About 1 second | "Data safety comparable to PostgreSQL" |
| None | Nothing on disk | Everything on restart | Pure cache |
fork() and copy-on-write: parent and child share memory pages, and a page is copied only when it changes. Fork itself blocks the main thread while the page table is copied: a 24 GB instance has a 48 MB page table. The AOF grows forever, so Redis rewrites it in the background (BGREWRITEAOF); since 7.0 it is "multi-part": one base file plus incremental files tracked by a manifest. If both are on, restart loads the AOF "since it is guaranteed to be the most complete".Fork time per GB of memory, by machine type
Milliseconds per GB, from the redis.io latency doc. Old Xen VMs were 10 to 40 times slower. Hover for exact numbers.
Show the full table
| Machine | RSS forked | Fork time (ms) | ms per GB |
|---|---|---|---|
| Linode (Xen) | 0.9 GB | 382 | 424 |
| EC2 old types (Xen) | 6.1 GB | 1460 | 239.3 |
| 6sync (KVM) | 360 MB | 8.2 | 23.3 |
| Physical (unknown HW) | 6.1 GB | 80 | 13.1 |
| VMware VM | 6.0 GB | 77 | 12.8 |
| EC2 new types (Xen) | 1 GB | 10 | 10 |
| Physical (Xeon 2.27 GHz) | 6.9 GB | 62 | 9 |
"Redis is durable" is a config choice, not a fact. Say which fsync policy, and how much loss you accept.
Source: Redis persistence · latency doc, fork table · FAQ (copy-on-write)
06Replication and Sentinel
A replica connects and sends PSYNC with the replication ID and the offset it has. An offset is a byte counter of the change stream. If the primary still has those bytes in its backlog (a ring buffer of recent changes), it sends only the gap. If not, it does a full sync: it makes an RDB file, sends it, then streams the buffered writes.
WAIT can ask for N acknowledged copies, but it "does not turn a set of Redis instances into a CP system": acknowledged writes can still be lost in a failover. Replicas are read-only by default since 2.6. Sentinel does four jobs: monitoring, notification, automatic failover and acting as a configuration provider (clients ask it for the current primary). The quorum is how many Sentinels must agree the primary is down; but the failover itself needs the vote of a majority of Sentinels. Run at least 3 Sentinels on 3 different machines. Default port is 26379.Sentinel gives high availability, not zero data loss. Say "async replication, small window of lost writes" in every answer.
Source: Redis replication · High availability with Redis Sentinel
07Redis Cluster: slots, redirects, limits
Sharding means splitting data across machines. Redis Cluster does it with hash slots. HASH_SLOT = CRC16(key) mod 16384. Each primary owns some slots. Clients learn the slot map and talk to the right node directly, with no proxy.
| Thing | What it means |
|---|---|
Hash tag {...} | Only the part inside braces is hashed. {user1000}.following and {user1000}.followers land in the same slot. |
-MOVED 3999 127.0.0.1:6381 | "This slot lives on that node, for good." Update your slot map. |
-ASK | "This slot is being moved right now. Ask the other node for this one query only." |
-TRYAGAIN | Multi-key command during resharding with keys split across nodes. |
| Multi-key commands | Only allowed when all keys hash to the same slot (else a CROSSSLOT error). |
| Cluster bus | Node-to-node port = data port + 10000; nodes gossip and vote on failover. |
Design keys for the cluster from day one: put things you change together under one hash tag, but do not put everything under one tag, or you create a hot shard.
Source: Redis cluster specification
Many commands at once
08Transactions, Lua and pipelining
| Tool | What it guarantees | What it does not |
|---|---|---|
MULTI / EXEC | Queued commands run in one go; no other client runs in the middle | No rollback: if one command fails after EXEC, the others still run. You cannot read a value and decide inside. |
WATCH | Optimistic lock: if a watched key changes before EXEC, EXEC returns null and you retry | Retries can pile up when many clients fight over one key |
Lua (EVAL) / Functions (7.0+) | Whole script is atomic; can read, decide and write on the server | A slow script blocks the whole server |
| Pipelining | Saves round trips and syscalls | Not atomic: other clients' commands can run between yours |
10,000 PINGs over loopback, with and without pipelining
Seconds, from the redis.io pipelining page (Ruby client, Mac OS X). Shorter is better.
Show the full table
| Run | Seconds |
|---|---|
| Without pipelining | 1.185238 |
| With pipelining | 0.250783 |
EXEC refuse the whole transaction (since 2.6.5); runtime errors like WRONGTYPE do not stop the rest. For scripts, "Redis guarantees the script's atomic execution … all server activities are blocked during its entire runtime". Pipelining helps beyond RTT because many commands share one read() and one write() syscall; throughput can reach about 10 times the baseline. With a 250 ms RTT and no pipelining you get at most 4 requests per second. Send pipelines in batches (the docs suggest about 10k commands) so reply buffers stay small.Need read-decide-write atomically? Use Lua or a Function. Need speed only? Use pipelining.
Source: Transactions · Scripting with Lua · Pipelining
09Pub/Sub vs Streams
| Question | Pub/Sub | Streams |
|---|---|---|
| Delivery | At-most-once | At-most-once or at-least-once |
| Subscriber offline? | Message is lost forever | Message waits in the stream |
| Stored? | No | Yes, trim with MAXLEN ~ 1000 |
| Work sharing | Every subscriber gets every message | Consumer group splits entries among workers |
| Crash recovery | None | Pending list + XPENDING, XCLAIM, XAUTOCLAIM |
| Good for | Live chat, cache-invalidation pings | Job queues, event processing |
<milliseconds>-<sequence> and stay increasing even if the clock jumps back. A consumer group gives each entry to one consumer; the entry stays in the Pending Entries List (PEL) until XACK. That is at-least-once: a worker that crashes after doing the work but before XACK will see the entry again, so handlers must be idempotent (safe to run twice). Sharded Pub/Sub (SSUBSCRIBE, 7.0+) keeps messages inside one shard instead of flooding the whole cluster bus.Pub/Sub for "tell whoever is online now". Streams for "this job must be done". Kafka when you need long retention and replay at large scale (see page 09).
Source: Redis Pub/sub · Redis Streams
Using it well
10Common patterns
SET k v EX 300allkeys-lru. Add TTL jitter.HSET sess:abc user 42 then EXPIRE sess:abc 1800. Needs persistence or replicas if logouts hurt.INCR + EXPIRE per user per secondZREMRANGEBYSCORE old ones, ZCARD. Wrap in Lua so it is atomic. The INCR doc shows the rate-limiter pattern.ZINCRBY, ZREVRANGE, ZREVRANKINCR / HINCRBY are atomicLPUSH + BRPOP. Reliable: BLMOVE or StreamsSET res token NX PX 30000-- KEYS[1]=rl:user:42 ARGV: now_ms, window_ms, limit, request_id
redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, ARGV[1] - ARGV[2])
if redis.call('ZCARD', KEYS[1]) < tonumber(ARGV[3]) then
redis.call('ZADD', KEYS[1], ARGV[1], ARGV[4])
redis.call('PEXPIRE', KEYS[1], ARGV[2])
return 1
end
return 0
INFO memory.Most Redis designs are one of these seven. Say which one, the data structure, and the TTL.
Source: INCR (rate limiter pattern) · Lists · FAQ memory footprint
11Distributed locks: SET NX, Redlock, and the debate
SET resource_name my_random_value NX PX 30000 # take lock for 30 s, only if free
-- release only if still mine (Lua, or DELEX ... IFEQ on Redis 8.4+)
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
The random value stops you deleting someone else's lock after yours expired. Redlock takes the same lock on 5 independent primaries and counts it as held only if a majority (3) said yes within the validity time.
Say: "Redis lock for avoiding duplicate work. For money or data correctness, fencing tokens checked by the database, or a consensus store."
Source: Distributed Locks with Redis · Kleppmann, How to do distributed locking · antirez, Is Redlock safe?
12Pitfalls in production
KEYS * is O(N) and blocks the one threadSCAN with a cursor, or keep your own index set.SMEMBERS, DEL, SUNION) block everyone. Find them with redis-cli --bigkeys / --memkeys (uses SCAN). Delete with UNLINK, which frees memory on a side thread.redis-cli --hotkeys (needs an LFU policy). Common fixes (practitioner advice, not redis.io): local in-process cache, read from replicas, split into key:1..N copies.vm.overcommit_memory=1 so fork does not fail.KEYS "a VERY common source of latency". It also says a 1 Gbit/s network adds ~200 µs and a Unix socket ~30 µs, far more than the sub-microsecond command time, which is why round trips matter more than CPU.Every pitfall here comes from one root: one thread. Anything O(N) or slow blocks everybody.
Source: KEYS · SCAN · redis-cli · memory optimization · latency doc
13Redis vs Memcached, and the licence story
| Feature | Redis | Memcached |
|---|---|---|
| Values | Strings, lists, hashes, sets, sorted sets, streams, more | Plain strings (bytes) |
| Max value | 512 MB string | 1 MB item by default (-I changes it) |
| Threads | One main thread for commands; optional I/O threads | Worker threads, 4 by default |
| Persistence | RDB, AOF | No RDB/AOF-style durability |
| Replication / failover | Built in: replicas, Sentinel, Cluster | None: "servers are unaware of each other"; the client hashes keys |
| Pick it when | You need structures, durability or pub/sub | Simple, multi-threaded string cache |
| Date | Event |
|---|---|
| 2024-03-20 | Redis Ltd: from Redis 7.4, dual licence RSALv2 / SSPLv1; no longer BSD-3. Versions up to 7.2 stay BSD. |
| 2024-03-28 | Linux Foundation launches Valkey, a fork from Redis 7.2.4 under BSD-3, backed by AWS, Google Cloud, Oracle, Ericsson, Snap. |
| 2025-05-01 | Redis adds OSI-approved AGPLv3 as a third option, starting with Redis 8. |
In interviews, "Redis or Valkey" is the same design answer. Licensing matters for cloud vendors, not for your design.
Source: docs.memcached.org · memcached configuring (4 threads) · memcached.c (1 MB limit) · Redis licence blog 2024 · Valkey launch · Redis AGPLv3 blog 2025
Interview prep
—When to use what
| Situation | Pick | Why |
|---|---|---|
| Cache in front of a DB | Redis, allkeys-lru, no or RDB persistence | Losing cache data only costs a refill |
| Redis holds the only copy | AOF everysec + RDB + replicas, noeviction | At most ~1 s of loss, backups, failover |
| Need HA, data fits one machine | Primary + replicas + 3 Sentinels | Simple, automatic failover |
| Data or writes exceed one machine | Redis Cluster | Shards across 16,384 slots |
| Read, decide, write atomically | Lua script / Function | Runs as one step on the server |
| Many independent commands | Pipelining | Cuts round trips and syscalls |
| Notify online users | Pub/Sub | Cheap, no storage |
| Jobs that must not be lost | Streams + consumer groups | PEL + XACK = at-least-once |
| Long retention, replay, huge scale | Kafka (page 09) | Disk-based log, partitions |
| Lock to avoid duplicate work | SET NX PX on one Redis | Cheap; rare double-run is acceptable |
| Lock for correctness | Consensus store + fencing tokens | Survives pauses and clock jumps |
—Common mistakes
—Interview questions
Show answer
epoll/kqueue) lets that thread serve thousands of sockets, only touching the ready ones. With pipelining one instance can do about 1 million requests per second (FAQ). Since 6.0, optional I/O threads do socket reads and writes, but commands still run on the main thread. Trade-off: one slow O(N) command blocks everyone. Follow-up: "How do you use more cores?" Run more instances or Cluster.Show answer
appendfsync everysec you lose at most about 1 second. always is safest but "very very slow". For a cache I would use RDB or nothing. If Redis holds real data I enable both, as redis.io recommends for "safety comparable to PostgreSQL": AOF for small loss, RDB for backups. At restart the AOF is loaded because it is the most complete. Follow-up: AOF rewrite and fork latency.Show answer
maxmemory-policy. The default, noeviction, rejects writes with an error but keeps serving reads. allkeys-lru or allkeys-lfu evict any key; volatile-* only evict keys with a TTL and act like noeviction if none have one. LRU and LFU are approximate: Redis samples 5 keys by default. For a cache I pick allkeys-lru, or allkeys-lfu if a small set is always hot. I also leave RAM for replication and AOF buffers, which are not counted against maxmemory. Follow-up: how to check the hit rate (keyspace_hits/keyspace_misses in INFO stats).Show answer
DEL, though replicas hide logically expired keys on reads. Accuracy is 0–1 ms since 2.6.Show answer
ZINCRBY on each score event, ZREVRANGE lb 0 9 WITHSCORES for the top 10, ZREVRANK for "my rank". All are O(log N), so 10 million is fine on one node. Ties are ordered by member name; if I need "first to reach wins", I encode time into the score (e.g. score + small fraction from inverse time). For daily/weekly boards, one key per period with a TTL. For durability, AOF + replica. If writes outgrow one node, shard by region or game, and merge top-K lists for a global view. Follow-up: "around me" view = rank then ZREVRANGE rank-5..rank+5.Show answer
rl:{user}:{minute}, INCR, set EXPIRE 60 on the first hit, reject if over 100. Cheap but allows up to 200 across a window edge. Better: sliding window log in a sorted set of request timestamps: remove entries older than 60 s, count with ZCARD, add if under limit. Do it in one Lua script so check-and-add is atomic; separate commands would race. Memory is O(limit) per user. Alternative: token bucket in a hash (tokens, last refill time) updated by Lua. Put the user id in a hash tag so the key stays on one slot. Follow-up: what if Redis is down? Fail open or closed, a business choice.Show answer
SET key value EX ttl; on update, write the DB then delete the key. A stampede happens when a hot key expires and thousands of requests hit the DB at once. Fixes: TTL jitter so keys don't expire together; a short lock (SET lock:key 1 NX PX 5000) so only one request rebuilds while others wait or serve stale; refresh-ahead before expiry. Also pick allkeys-lru and watch the hit ratio. Follow-up: why delete instead of update on writes? Avoids racing writers leaving an old value.Show answer
slot = CRC16(key) mod 16384 (XMODEM CRC16, 14 of 16 bits). Each primary owns a range of slots. Clients cache the slot-to-node map and go straight to the owner; no proxy. If only part of the key is inside {}, only that part is hashed, so {user42}:cart and {user42}:profile share a slot. Multi-key commands, transactions and Lua only work when all keys are in one slot, else CROSSSLOT. Adding a node means moving slots, not rehashing everything. Suggested max is about 1000 nodes. Follow-up: what about a hash tag shared by everyone? It creates one hot slot.Show answer
-MOVED slot host:port means the slot now belongs to another node permanently; the client should update its slot map and retry there. -ASK happens only during resharding: the slot is being migrated, this key may already be on the target, so the client sends ASKING plus the command to the target once, without changing its map. During migration, a multi-key command whose keys are split between source and target gets -TRYAGAIN. A complete cluster client must handle both. Why two errors? So clients don't flip their map back and forth mid-migration.Show answer
Show answer
WRONGTYPE), the others still run. Syntax errors while queuing make EXEC refuse the whole batch. You can't use a read result inside the transaction; WATCH gives optimistic locking: if a watched key changes, EXEC returns null and you retry. A Lua script (or Function in 7.0+) is atomic too, but can read, branch and write on the server, so it's usually simpler and faster. Risk: a long script blocks the server; keep scripts short and pass every key in KEYS.Show answer
XACK, so you get at-least-once and crash recovery via XAUTOCLAIM; limits are RAM and one node per stream key. Kafka is a disk-based partitioned log with long retention and replay, built for very high volume and many independent consumer groups. My rule: Pub/Sub for "who is online now", Streams for a moderate job queue already on Redis, Kafka for event backbones and analytics. Handlers must be idempotent for at-least-once.Show answer
everysec a crash can also lose up to about a second. WAIT N timeout blocks until N replicas acked, which shrinks the window, but docs say it does not make Redis a CP system. min-replicas-to-write and min-replicas-max-lag reject writes when replicas lag, bounding the loss. Cluster spec: "always a window … to lose writes during partitions". So: don't use Redis as the only store for money; use it with a durable DB.Show answer
SET res <random> NX PX 30000; release with a Lua check-and-delete (or DELEX … IFEQ in 8.4+) so you never delete someone else's lock. On one instance this fails if the primary dies before replicating. Redlock takes the lock on a majority of 5 independent primaries within the validity time. Kleppmann's critique: a GC pause or network delay longer than the TTL lets two holders act, and Redlock assumes bounded clocks and delays; it has no fencing tokens. Fix: storage rejects writes with a token lower than the last seen. My answer: Redis lock for efficiency (avoid double work); for correctness, a consensus system (ZooKeeper/etcd) with fencing tokens.Show answer
KEYS is O(N) over the whole keyspace and runs on the single command thread, so every other client waits until it finishes; the docs call it a very common source of latency and say not to use it in app code. Use SCAN cursor MATCH p COUNT n: each call is O(1) and returns a few keys plus a new cursor; repeat until the cursor is 0. Guarantees: keys present throughout are returned; a key may be returned more than once; keys added or removed mid-scan may or may not appear. Better still, keep your own index (a set of ids) so you never need to search. Follow-up: SSCAN, HSCAN, ZSCAN for big collections.Show answer
redis-cli --hotkeys (needs an LFU policy) or client metrics. In a cluster the slot owner overloads while others idle, and adding nodes doesn't help because one key can't be split by slots. Fixes for reads: a small in-process cache with a short TTL in each app server; read from replicas; or store N copies (key:1..N) and pick one at random. For writes such as a counter: split into N sub-counters and sum on read. Note these are practitioner techniques, not from redis.io. Also check whether the key is big, since hot + big is worst.Show answer
SMEMBERS, HGETALL, SUNION) block the main thread; it also makes replication and migration slow and can unbalance cluster memory. DEL frees every element on the main thread, which can stall Redis. Use UNLINK, which removes the key now and frees memory on a background thread. Find big keys with redis-cli --bigkeys or --memkeys, which use SCAN. Prevent them by splitting: e.g. followers:{id}:{bucket}, and iterate with SSCAN.Show answer
save points and AOF rewrites call fork(), which copies the page table on the main thread: about 10–13 ms per GB on modern hardware, but 239–424 ms per GB on old Xen VMs (latency doc). Check latest_fork_usec in INFO. Transparent huge pages make copy-on-write much worse; disable them. Also check mass expiry (many keys with the same TTL), slow commands via SLOWLOG, swapping, and AOF fsync on a busy disk. Fixes: bigger interval, persistence on a replica, disable THP, modern VMs.Show answer
sess:{id} with user id, roles, CSRF token; EXPIRE 30 min, refreshed on each request (sliding expiry). Size: FAQ says 1 million 5-field hashes take ~160 MB, so 10 million ≈ 1.6 GB plus overhead; one node fits, but I'd use a primary + replicas + Sentinel for availability, or Cluster if growth is large. Persistence: AOF everysec, since losing sessions logs everyone out. Policy volatile-lru or noeviction with alerts, since every session has a TTL. Logout = DEL. "Log out everywhere" = keep a set user:{id}:sessions.Show answer
PFADD hll:{page}:{date} visitor_id and PFCOUNT. Each HLL uses at most 12 KB with a 0.81% standard error, no matter how many visitors. Weekly uniques: PFMERGE seven daily keys, which is correct for unions. Set a TTL on old days. If you need exact counts for a few pages, use a set for those only. If you need "did user X visit?" per day with integer ids, use a bitmap instead: 1 bit per user (100 million users ≈ 12 MB per docs). Trade-off: HLL can't list members.Show answer
Show answer
—Cheat sheet
| Fact | Value | Source |
|---|---|---|
| Command threads | 1 main thread; I/O threads optional (6.0+, off by default) | redis.conf, 6.0 notes |
| Max string value | 512 MB | strings doc |
| Max keys per instance | 2^32 (tested 250 million) | FAQ |
| Default eviction | noeviction | redis.conf |
| LRU sample size | maxmemory-samples 5 | eviction doc |
| Active expiry | 10×/s, 20 keys, repeat if > 25% expired | latency doc |
| AOF default fsync | everysec → lose ≤ ~1 s | persistence doc |
| Default RDB save points | 3600 s/1, 300 s/100, 60 s/10000 changes | redis.conf |
| Replication | Async; PSYNC with replication ID + offset | replication doc |
| Sentinel | ≥ 3 instances; port 26379; quorum detects, majority authorises | Sentinel doc |
| Cluster slots | 16,384; CRC16 (XMODEM); ~1000 nodes max suggested; bus = port + 10000 | cluster spec |
| HyperLogLog | ≤ 12 KB, 0.81% error | HLL doc |
| Pipelining batch advice | ~10k commands per batch | pipelining doc |
| Pub/Sub delivery | At-most-once | pubsub doc |
| Memcached | 4 threads default, 1 MB items, no replication | memcached docs/source |
Reference
—Sources
| What | Where |
|---|---|
| Eviction policies, LRU/LFU/LRM | redis.io/docs/latest/develop/reference/eviction/ |
| RDB, AOF, fsync, multi-part AOF | redis.io persistence |
| Replication, PSYNC, WAIT | redis.io replication |
| Sentinel quorum and majority | redis.io sentinel |
| Cluster slots, MOVED/ASK, write safety | redis.io cluster spec |
| Transactions, WATCH | redis.io transactions |
| Lua atomicity, Functions | redis.io eval intro |
| Pipelining numbers | redis.io pipelining |
| Pub/Sub delivery semantics | redis.io pubsub |
| Streams, PEL, XACK | redis.io streams |
| Expiry, KEYS, SCAN, GEOADD, INCR | redis.io commands |
| Latency, fork table, expiry cycle | redis.io latency |
| Memory, fragmentation, listpack | redis.io memory optimization |
| FAQ: footprint, threads, limits | redis.io FAQ |
| I/O threads (6.0) | Redis 6.0 release notes, redis.conf |
| Distributed locks, Redlock | redis.io distributed locks |
| Redlock critique | Martin Kleppmann, 2016-02-08; reply antirez |
| Licence change, Valkey, AGPL | Redis 2024-03-20, Linux Foundation 2024-03-28, Redis 2025-05-01 |
| Memcached facts | docs.memcached.org, configuring, memcached.c |
Course