GAP·MAP

Redis in practice

[ middle depth ]

Picking a data structure and the O(1) vs O(N) trap

Redis stores typed structures, and the type you pick sets your complexity. Strings hold bytes or counters (GET/SET/INCR are O(1)). Hashes map fields to values (HGET/HSET O(1) per field). Lists push and pop at the ends in O(1) (queues, stacks). Sets test membership in O(1) (tags, unique visitors). Sorted sets keep members ordered by a float score (ZADD O(log N), a ranged read O(log N + M)), which is why leaderboards and rate limiters use them. Streams are an append-only log with consumer groups.

The trap is the whole-collection command. SMEMBERS on a million-element set, LRANGE 0 -1, HGETALL, and SORT are O(N), and because Redis runs commands on one thread, that scan stalls every other client for its full duration. Reach for bounded access instead: SSCAN/HSCAN/ZSCAN, or ZRANGE over a small range.

Why KEYS is banned in production

KEYS pattern walks the entire keyspace every call, O(N) in the number of keys, on the single command thread.

KEYS session:*                      # visits every key in the DB, blocks all clients
SCAN 0 MATCH session:* COUNT 100    # cursor, small batch, yields between calls

Wrong "KEYS is fine if the pattern is specific enough." The pattern only filters the output; Redis still visits every key to test it, so a narrow pattern blocks for the same duration. Right use SCAN, a cursor-based iterator that returns small batches and lets other commands run in between. It gives weaker guarantees (it can return duplicates, and you loop until the cursor returns 0), the price of not freezing the server. Free big keys with UNLINK (background reclaim) rather than DEL.

Persistence: RDB vs AOF you can configure

RDB writes a point-in-time snapshot. Automatic snapshots use BGSAVE, which forks a child that writes via copy-on-write while the parent keeps serving, so a crash loses every write since the last snapshot (minutes). AOF logs each write command and replays it on restart. Its durability knob is appendfsync.

appendfsync everysec   # default: fsync ~once/sec, lose at most ~1s on a crash
appendfsync always     # fsync every write batch: safest, much slower
appendfsync no          # let the OS flush (~30s): fastest, least safe

Wrong "appendfsync always is the default." The default is everysec. And if you enable both RDB and AOF, Redis loads the AOF on restart because it is the more complete record.

Eviction defaults that bite

maxmemory 0 means unlimited on 64-bit, and the default maxmemory-policy is noeviction: once the cap is hit, writes return errors while reads still work. To run Redis as a cache you set a cap and an evicting policy such as allkeys-lru.

Wrong "volatile-lru will free memory when the cache fills." The volatile-* policies only consider keys that carry a TTL. If none of your keys have one, they behave like noeviction and fail writes. Use allkeys-* for a pure cache; keep volatile-* for a mixed store where only the expiring keys may be dropped.

[ senior depth ]

The single thread as feature and footgun

One thread runs the event loop and executes commands to completion, one at a time. That is why INCR, SET NX, MULTI/EXEC, and Lua scripts are atomic with no locking: nothing else runs while they do. The same property is a footgun: a single O(N) command (KEYS, SORT, a huge SMEMBERS, a synchronous SAVE) is a head-of-line block that freezes every client for its full runtime.

Wrong "Redis 6 made command execution multi-threaded, so slow commands stopped blocking." Threaded I/O (io-threads, opt-in and off by default) parallelizes socket reads, writes, and protocol parsing only; command execution stays strictly single-threaded, which preserves the atomicity above.

Pipelining is a separate axis (batch commands to cut round trips, no atomicity); MULTI/EXEC and Lua run atomically, and only Lua folds a read-compute-write into one round trip.

Persistence durability vs tail latency

The durability choice is a latency choice. appendfsync always fsyncs every write batch (safest, slow); everysec risks about a second of loss; RDB alone risks minutes. The subtle cost is the RDB fork: on a large dataset under write load, BGSAVE's copy-on-write forces the parent to duplicate pages as it writes, and the fork itself can add a multi-hundred-millisecond spike. That tail latency, not throughput, is what pages you.

Redlock and the correctness-lock debate

A single-instance lock is SET resource <random-token> NX PX 30000, released by comparing the token then deleting. A master plus replica is not safe, because replication is asynchronous: a client locks the master, it dies before propagating, and a promoted replica hands the same lock to a second client. Redlock answers with five independent masters (no replication between them), holding the lock only on a majority acquired within the TTL; effective validity is the TTL minus elapsed minus clock drift, and a crashed node must fsync or delay restart past the max TTL, or a majority re-forms behind its back.

Wrong "Redlock is a correct mutual-exclusion primitive." It issues no monotonically increasing fencing token, so a process that pauses (GC or STW) past the wall-clock TTL can resume after the lock expired while a second holder runs. Kleppmann's fix for correctness locks is a fencing token the resource rejects when it goes backwards, backed by a consensus system such as ZooKeeper. antirez replies that a pause after validation threatens every lock system, and that Redis should move to a monotonic clock. For efficiency locks a single Redis is fine; for correctness, fencing at the resource is the load-bearing part.

Cluster vs Sentinel

Cluster shards; Sentinel does not. In a cluster the keyspace is 16384 hash slots, HASH_SLOT = CRC16(key) mod 16384, and multi-key ops must land in one slot (use {hash tags} to co-locate keys). Clients follow MOVED (slot moved permanently, update your slot map) and ASK (one-shot during migration, send ASKING first, do not update the map). Replication is async, so acknowledged writes can be lost on failover. Sentinel gives high availability for a single master with replicas: its quorum only declares a master objectively down, while performing the failover needs a majority of all Sentinels to elect a leader, so run at least three on independent machines or a minority partition cannot fail over even when quorum is met.

beta

The interviewer part is in the works.

The diagnostic, personal maps, and AI mock interviews are being finished right now. The notes stay free either way. Leave an email and you'll get the first-cohort invite, plus a month of Pro when it opens.

builds on

more Databases

was this useful?