Strings, hashes, lists, sets, sorted sets and streams — and the features each one makes trivial.
Published September 24, 2026
Redis is often introduced as "a cache", but its real strength is that it's an in-memory data structure server. Instead of storing opaque values, it offers strings, hashes, lists, sets, sorted sets, streams and more, each with atomic operations that run in microseconds. Choosing the right structure turns many backend features (leaderboards, rate limiting, deduplication, job queues) into one or two commands.
Everything lives in RAM, and commands execute one at a time on a single main thread. That design explains both its speed (no locking, no disk seeks on the request path) and its main constraints (dataset size is bounded by memory, and one slow command blocks everyone).
A string holds up to 512 MB: text, serialized JSON, or a number.
SET session:9f2c "{...}" EX 1800 # value with a 30-minute expiry
INCR page:views:home # atomic counter: no read-modify-write race
SET lock:report "worker-7" NX PX 30000 # set only if absent, auto-expires: a simple lock
INCR, and SET … NX, are atomic, which removes whole classes of race conditions that would need transactions elsewhere. Always set expirations on cache-like keys. A Redis instance without TTLs slowly fills up.
A hash maps field names to values under one key, like a lightweight row:
HSET user:42 name "Asha" plan "pro" logins 17
HINCRBY user:42 logins 1
HGET user:42 plan
Hashes let you read or update individual fields without rewriting a serialized blob, and small hashes are stored very compactly.
Lists are ordered, and support O(1) pushes and pops at both ends:
LPUSH recent:user:42 "product:981" # newest first
LTRIM recent:user:42 0 19 # keep only the last 20 viewed items
LRANGE recent:user:42 0 9 # show 10
BRPOP jobs 5 # blocking pop: a simple work queue
LPUSH combined with LTRIM is the idiomatic "last N items" pattern. Lists work as basic queues, but a popped job is lost if the worker crashes before finishing it. For reliable processing, use Streams (below).
SADD online:users 42 77 105
SISMEMBER online:users 42 # O(1) membership test
SINTER tags:java tags:spring # items tagged with both
Sets are good for de-duplication, tagging, and "who's online" style features. For counting distinct items at huge scale (unique visitors per day), HyperLogLog (PFADD/PFCOUNT) estimates cardinality within about 1% error, using only around 12 KB per counter, instead of storing every ID.
A sorted set keeps unique members ordered by a numeric score, with O(log n) updates and range queries:
ZINCRBY leaderboard:weekly 50 "player:42" # add points
ZREVRANGE leaderboard:weekly 0 9 WITHSCORES # top 10
ZREVRANK leaderboard:weekly "player:42" # this player's position
Using timestamps as scores turns sorted sets into time-ordered indexes. That's the basis of a precise sliding-window rate limiter:
ZREMRANGEBYSCORE ratelimit:user:42 0 (now - 60000) # drop requests older than 60 s
ZADD ratelimit:user:42 now requestId # record this request
ZCARD ratelimit:user:42 # how many in the window?
EXPIRE ratelimit:user:42 60
Wrap these in a Lua script (or a MULTI/EXEC transaction), so the check and the insert happen atomically. Redis runs Lua scripts without interleaving other commands.
Streams are an append-only log with consumer groups, similar in spirit to Kafka topics, built into Redis:
XADD orders * orderId 981 total 129.49 # append an event
XGROUP CREATE orders billing $ MKSTREAM # a consumer group
XREADGROUP GROUP billing worker-1 COUNT 10 STREAMS orders >
XACK orders billing 1712345678901-0 # confirm processing
Messages delivered to a consumer stay pending until acknowledged. If a worker crashes, another can claim its pending messages (XAUTOCLAIM). That gives at-least-once processing, which plain lists can't. It's good for moderate-volume event processing and job queues when you already run Redis.
In-memory doesn't have to mean volatile, but you must choose a durability setting:
appendfsync everysec, you lose at most about one second of writes on a crash.Replication (a primary with replicas, plus Sentinel or a managed service for automatic failover) protects availability. Replication is asynchronous, though, so a failover can lose the last few acknowledged writes. Don't use Redis as the only system of record for money or other critical state.
maxmemory, and choose an eviction policy: allkeys-lru or allkeys-lfu for caches, noeviction for data that must never silently disappear.KEYS *, SMEMBERS on huge sets, and unbounded LRANGEs block every other client. Use SCAN-family commands for iteration, and bound all range reads.{user:42}:cart and {user:42}:profile land in the same slot.Q: Why is Redis so fast? A: Data lives in memory, commands run on a single thread without lock contention, the data structures are highly optimized, and network I/O is handled efficiently. Most operations complete in microseconds, so the network round trip usually dominates.
Q: How would you build a leaderboard with Redis?
A: Use a sorted set, with players as members and scores as points. ZINCRBY updates scores atomically, ZREVRANGE returns the top N, and ZREVRANK gives a player's position, each in O(log n) time. Use separate keys per period (daily or weekly), with expiry.
Q: When should you use Streams instead of Lists for a queue? A: When you can't afford to lose jobs. Streams keep delivered messages pending until they're acknowledged, support consumer groups, and let other workers claim messages from crashed consumers. Lists remove an item on pop, so a crash mid-processing loses it.
Q: Can Redis be your primary database? A: For some workloads, yes: sessions, leaderboards, counters, or ephemeral state, especially with AOF persistence and replication. For critical records that need strong durability guarantees and complex queries, keep a primary database, and use Redis alongside it. Asynchronous replication and memory limits make it a risky sole system of record.
Q: What are hash tags in Redis Cluster?
A: A section of the key in braces, such as {user:42}. Only that part is hashed to choose the slot, so keys sharing a hash tag land on the same node. That makes multi-key operations and Lua scripts across those keys possible in a cluster.