A lot of people think Redis is “just a cache.” That’s selling it short. Redis is an in-memory data structure store — it gives us strings, hashes, lists, sets, sorted sets, streams, and more. Each data type has its own set of commands, and picking the right one can make a huge difference in how we solve a problem.
Let’s walk through each one.
Strings
The simplest type. A string in Redis can hold text, numbers, or even binary data (up to 512 MB). But we mostly use it for simple key-value caching and counters.
# Basic set and get
SET user:name "Manish"
GET user:name # "Manish"
# Set with expiry (seconds)
SETEX session:abc123 3600 "user_42" # expires in 1 hour
# Set only if key doesn't exist (useful for locks)
SETNX lock:order:99 "worker_1" # returns 1 if set, 0 if already exists
# Atomic counter — no race conditions
SET page:views 0
INCR page:views # 1
INCR page:views # 2
INCRBY page:views 10 # 12
Use cases: simple cache, session tokens, counters, rate limiting.
Hashes
Think of a hash as a mini object or dictionary stored under one key. Instead of serializing a whole JSON object into a string, we can store each field separately — and update individual fields without rewriting the whole thing.
# Store a user profile
HSET user:42 name "Manish" age 28 city "Pune"
# Get a single field
HGET user:42 name # "Manish"
# Get all fields
HGETALL user:42 # name, Manish, age, 28, city, Pune
# Update just one field
HSET user:42 age 29
# Increment a numeric field
HINCRBY user:42 age 1 # 30
Use cases: user profiles, product details, any object with multiple fields that we update independently.
Lists
Redis lists are linked lists — fast push/pop at both ends, but slow random access in the middle. They’re perfect for queues and “recent items” feeds.
# Build a queue (push left, pop right = FIFO)
LPUSH queue:emails "email_1"
LPUSH queue:emails "email_2"
RPOP queue:emails # "email_1" (first in, first out)
# Blocking pop — waits up to 5 seconds for a new item
BLPOP queue:emails 5
# Recent activity feed (keep last 100 items)
LPUSH feed:user:42 "liked a post"
LTRIM feed:user:42 0 99 # trim to 100 items
LRANGE feed:user:42 0 9 # get the 10 most recent
Use cases: message queues, recent activity, job queues, chat history.
Sets
Unordered collections of unique values. Sets shine when we need to check membership or compute intersections/unions.
# Track tags on a post
SADD post:1:tags "redis" "database" "caching"
SADD post:2:tags "redis" "performance" "caching"
# Check if a tag exists
SISMEMBER post:1:tags "redis" # 1 (true)
# All tags on post 1
SMEMBERS post:1:tags
# Tags common to both posts
SINTER post:1:tags post:2:tags # "redis", "caching"
# All unique tags across both posts
SUNION post:1:tags post:2:tags
Use cases: tags, unique visitors, mutual friends, “already processed” tracking.
Sorted Sets
Like sets, but every member has a score. Redis keeps them sorted by score automatically. This is what makes leaderboards and priority queues trivial.
# Leaderboard
ZADD leaderboard 1500 "alice"
ZADD leaderboard 2300 "bob"
ZADD leaderboard 1800 "charlie"
# Top 3 players (highest score first)
ZREVRANGE leaderboard 0 2 WITHSCORES
# bob 2300, charlie 1800, alice 1500
# What's alice's rank? (0-based, highest first)
ZREVRANK leaderboard "alice" # 2
# Increment score
ZINCRBY leaderboard 1000 "alice" # alice now at 2500
Use cases: leaderboards, priority queues, rate limiting (sliding window), scheduling.
Streams
Streams are Redis’s answer to event logs and message queues. Unlike Pub/Sub, streams persist messages and support consumer groups — so we can have multiple consumers processing messages reliably.
# Add events to a stream
XADD orders * product "laptop" qty 1
XADD orders * product "mouse" qty 3
# Read all entries
XREAD COUNT 10 STREAMS orders 0
# Consumer groups — multiple workers processing orders
XGROUP CREATE orders workers 0
XREADGROUP GROUP workers worker-1 COUNT 1 STREAMS orders >
Use cases: event sourcing, audit logs, reliable message queues, activity streams.
HyperLogLog
This one is clever. HyperLogLog counts approximate unique items using very little memory — about 12 KB regardless of how many items we add. The trade-off is it’s ~0.81% off from the exact count.
# Count unique visitors
PFADD visitors:today "user_1" "user_2" "user_3"
PFADD visitors:today "user_1" # duplicate, not counted again
PFCOUNT visitors:today # 3
# Merge multiple days
PFMERGE visitors:week visitors:mon visitors:tue visitors:wed
Use cases: unique visitor counting, unique search queries, cardinality estimation.
Bitmaps
Bitmaps let us set individual bits on a string. Each bit is either 0 or 1. Super memory-efficient for tracking boolean states across millions of users.
# Track daily active users (user ID = bit offset)
SETBIT active:2026-03-31 42 1 # user 42 was active today
SETBIT active:2026-03-31 99 1 # user 99 was active today
# Was user 42 active?
GETBIT active:2026-03-31 42 # 1
# How many users were active today?
BITCOUNT active:2026-03-31 # 2
Use cases: feature flags, daily active users, bloom filter implementation.
Data Type Comparison
| Type | Best For | Key Commands |
|---|---|---|
| String | Cache, counters, locks | SET, GET, INCR, SETNX |
| Hash | Objects, user profiles | HSET, HGET, HGETALL |
| List | Queues, recent items | LPUSH, RPOP, BLPOP |
| Set | Unique items, tags | SADD, SINTER, SUNION |
| Sorted Set | Leaderboards, rankings | ZADD, ZRANGE, ZREVRANK |
| Stream | Event logs, message queues | XADD, XREAD, XREADGROUP |
| HyperLogLog | Unique counts (~0.81% error) | PFADD, PFCOUNT, PFMERGE |
| Bitmap | Feature flags, DAU tracking | SETBIT, GETBIT, BITCOUNT |
Internal Encodings (Brief)
Redis doesn’t always use the “obvious” data structure under the hood. For small data, it uses compact encodings to save memory:
- Small hashes/lists/sets use listpack (previously ziplist) — a flat, compact byte array. Much less memory than a real hash table or linked list.
- Large sorted sets use a skiplist — a probabilistic data structure that gives us O(log n) lookups, similar to a balanced tree.
- Large hashes switch to a real hashtable once they exceed a threshold.
We usually don’t need to worry about this — Redis picks the best encoding automatically. But it’s good to know for interviews when someone asks “how does Redis store a sorted set internally?”