Redis is an in-memory key-value store that supports rich data structures. It is commonly used for caching, message queues, distributed locks, leaderboards, and more. This guide starts from installation and covers all core data types and real-world practices.
Installation and Connection
macOS
brew install redis
brew services start redis # start in background
redis-cli # open interactive client
Linux (Ubuntu / Debian)
sudo apt update && sudo apt install redis-server
sudo systemctl start redis
redis-cli
Docker (recommended — clean environment)
# Start container
docker run -d --name redis -p 6379:6379 redis:7-alpine
# Open CLI
docker exec -it redis redis-cli
Basic Connection and Verification
redis-cli -h 127.0.0.1 -p 6379
# Test connectivity
PING # returns PONG
# If a password is set
AUTH your_password
# Select a database (default 0, 16 total)
SELECT 1
# Count keys in current database
DBSIZE
# List all keys (avoid on production — can block)
KEYS *
# Pattern scan (use SCAN instead of KEYS on production)
SCAN 0 MATCH user:* COUNT 100
Data Types Overview
| Type | Typical Use Cases |
|---|---|
| String | Cache, counters, distributed locks |
| Hash | Object storage (user profiles, product details) |
| List | Message queue, activity feed, operation log |
| Set | Deduplication, mutual friends, lottery |
| ZSet (Sorted Set) | Leaderboards, weighted queues |
| Stream | Persistent message queue (lightweight Kafka alternative) |
| Bitmap | User check-in, Bloom filter |
| HyperLogLog | Large-scale UV estimation (approximate) |
String
String is the most basic type. It can store text, numbers, or binary data (up to 512 MB).
# Set and get
SET name "Alice"
GET name # "Alice"
# Set with expiration (seconds)
SET token "abc123" EX 3600
# Set only if key does not exist (basis of distributed lock)
SET lock "1" NX EX 10
# Batch operations
MSET k1 v1 k2 v2 k3 v3
MGET k1 k2 k3
# Append to value
APPEND name " Smith" # "Alice Smith"
# Numeric operations
SET counter 0
INCR counter # 1
INCRBY counter 5 # 6
DECR counter # 5
DECRBY counter 2 # 3
# Float increment
SET price 9.99
INCRBYFLOAT price 0.5 # 10.49
# Get string length
STRLEN name
# Get old value and set new value atomically
GETSET name "Bob" # returns "Alice Smith", sets "Bob"
Hash
Hash is ideal for storing structured objects, avoiding repeated serialization and deserialization.
# Set fields
HSET user:1 name "Alice" age 25 email "alice@example.com"
# Get a single field
HGET user:1 name # "Alice"
# Get all fields and values
HGETALL user:1
# Get all field names
HKEYS user:1
# Get all values
HVALS user:1
# Number of fields
HLEN user:1
# Check if a field exists
HEXISTS user:1 email # 1 (exists)
# Delete a field
HDEL user:1 email
# Get multiple fields at once
HMGET user:1 name age
# Increment a numeric field
HINCRBY user:1 age 1
# Set only if field does not exist
HSETNX user:1 role "admin"
List
List is a doubly-linked list. It supports push and pop from both ends — great for queues and stacks.
# Push to the left (head)
LPUSH logs "event1" "event2" "event3"
# Push to the right (tail)
RPUSH queue "task1" "task2"
# Pop from the left
LPOP logs
# Pop from the right
RPOP queue
# Blocking pop (0 = wait forever — great for consumer pattern)
BLPOP queue 0
BRPOP queue 30 # wait up to 30 seconds
# View a range (0 -1 = all)
LRANGE logs 0 -1
# Get element at index
LINDEX logs 0
# Get list length
LLEN logs
# Trim to a range (keep only first 100 elements)
LTRIM logs 0 99
# Insert before or after an element
LINSERT logs BEFORE "event2" "new-event"
# Remove matching elements
LREM logs 2 "event1" # remove 2 occurrences of "event1"
Classic use: message queue
# Producer
RPUSH queue "msg1"
# Consumer (blocking — returns immediately when a message arrives)
BLPOP queue 0
Set
Set is an unordered collection of unique members. It supports set operations.
# Add members
SADD tags "redis" "cache" "nosql"
# Check membership
SISMEMBER tags "redis" # 1
# Get all members
SMEMBERS tags
# Set size
SCARD tags
# Remove a member
SREM tags "nosql"
# Get random members (no removal)
SRANDMEMBER tags 2
# Pop a random member
SPOP tags
# Set operations
SADD set1 "a" "b" "c"
SADD set2 "b" "c" "d"
SUNION set1 set2 # union: a b c d
SINTER set1 set2 # intersection: b c
SDIFF set1 set2 # difference: a (in set1 but not set2)
# Store results in a new key
SUNIONSTORE result set1 set2
SINTERSTORE result set1 set2
ZSet (Sorted Set)
Every member in a ZSet has a score. Members are sorted by score — perfect for leaderboards.
# Add members (score member)
ZADD leaderboard 1000 "Alice" 850 "Bob" 1200 "Charlie"
# Get a member's score
ZSCORE leaderboard "Alice" # 1000
# Rank by score ascending (0 = first place)
ZRANK leaderboard "Alice" # 1 (0-indexed)
# Rank by score descending
ZREVRANK leaderboard "Alice" # 1
# Get range by rank ascending
ZRANGE leaderboard 0 -1 WITHSCORES
# Get range by rank descending
ZREVRANGE leaderboard 0 2 WITHSCORES
# Get range by score
ZRANGEBYSCORE leaderboard 800 1100 WITHSCORES
# Increment score
ZINCRBY leaderboard 100 "Alice" # 1100
# Total member count
ZCARD leaderboard
# Count members within a score range
ZCOUNT leaderboard 900 1200
# Remove a member
ZREM leaderboard "Bob"
# Remove by rank range
ZREMRANGEBYRANK leaderboard 0 1
# Remove by score range
ZREMRANGEBYSCORE leaderboard 0 800
Expiration
# Set expiration in seconds
EXPIRE key 3600
# Set expiration in milliseconds
PEXPIRE key 3600000
# Set to a specific Unix timestamp (seconds)
EXPIREAT key 1893456000
# Check remaining TTL in seconds (-1 = no expiry, -2 = key does not exist)
TTL key
# Check remaining TTL in milliseconds
PTTL key
# Remove expiration (make permanent)
PERSIST key
Persistence
Redis is in-memory by default — data is lost on restart. Two persistence options exist:
RDB (Snapshot)
Periodically dumps memory to disk. The default persistence method.
# redis.conf — save every 60s if at least 1 key changed
save 60 1
save 300 10
save 900 1
# Trigger manually
BGSAVE # async background save
SAVE # synchronous (blocks — avoid on production)
Pros: compact file, fast restore.
Cons: data between two snapshots can be lost.
AOF (Append-Only File)
Logs every write command. Replays on restart to restore data.
# redis.conf
appendonly yes
appendfilename "appendonly.aof"
# Sync policy
appendfsync always # sync on every write — safest, slowest
appendfsync everysec # sync once per second (recommended — up to 1s data loss)
appendfsync no # let the OS decide — fastest but riskiest
Pros: safer, at most 1 second of data loss.
Cons: larger file, slower restore.
Hybrid Persistence (recommended — Redis 4.0+)
# redis.conf
aof-use-rdb-preamble yes
The AOF file starts with an RDB snapshot followed by incremental commands — combining speed and safety.
Pub/Sub
# Subscribe to channels (blocks waiting for messages)
SUBSCRIBE news sports
# Publish a message
PUBLISH news "Breaking: Redis 8.0 released"
# Pattern subscribe
PSUBSCRIBE news.*
# Inspect subscriptions
PUBSUB CHANNELS # list active channels
PUBSUB NUMSUB news # subscriber count for a channel
Note: Pub/Sub is fire-and-forget with no persistence. For reliable messaging, use Stream or an external MQ.
Transactions
Redis transactions use MULTI / EXEC to guarantee atomic execution of a command batch (but there is no rollback on runtime errors).
MULTI # start transaction
SET k1 "v1" # queued
SET k2 "v2"
INCR counter
EXEC # execute all queued commands
# Discard the transaction
DISCARD
WATCH (optimistic lock):
WATCH balance # monitor the key
MULTI
DECRBY balance 100
EXEC # returns nil if balance was modified between WATCH and EXEC
Pipeline (Batched Requests)
Pipeline bundles multiple commands into a single round trip, significantly boosting throughput.
# redis-cli example
redis-cli --pipe << EOF
SET k1 v1
SET k2 v2
INCR counter
EOF
All major client libraries support Pipeline. Example with Node.js (ioredis):
const pipeline = redis.pipeline()
pipeline.set('k1', 'v1')
pipeline.set('k2', 'v2')
pipeline.incr('counter')
await pipeline.exec()
Common Use Cases
Caching
# Cache-Aside pattern:
# Read: check Redis first; on miss, query DB and write to Redis
# Write: update DB first, then delete the Redis cache (ensures consistency)
SET user:1:profile "{...}" EX 300
Distributed Lock
# Acquire lock: NX ensures only one client succeeds, EX prevents dead lock
SET lock:order:123 "client-uuid" NX EX 30
# Release lock: verify it is still your lock before deleting
# Use a Lua script for atomicity
EVAL "
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
" 1 lock:order:123 client-uuid
Rate Limiting
# Limit a user to 100 requests per minute
INCR rate:user:123
EXPIRE rate:user:123 60
# Atomic initialization + increment
SET rate:user:123 0 EX 60 NX # initialize on first request
INCR rate:user:123 # increment on each request
Leaderboard
# Update score
ZINCRBY leaderboard 10 "user:123"
# Get top 10
ZREVRANGE leaderboard 0 9 WITHSCORES
# Get a user's rank
ZREVRANK leaderboard "user:123"
Session Storage
SET session:abc123 "{userId: 1, role: admin}" EX 86400
GET session:abc123
EXPIRE session:abc123 86400 # renew session
The Three Cache Failure Modes
Cache Penetration
Symptom: Requests for keys that exist in neither cache nor database always reach the database.
Solutions:
- Cache null values: store an empty placeholder in Redis with a short TTL
- Bloom filter: reject requests for keys that definitely do not exist before they reach Redis
Cache Breakdown
Symptom: A hot key expires, and a burst of concurrent requests simultaneously hit the database.
Solutions:
- Keep hot keys alive (no expiry, or logical expiry with async refresh)
- Mutex lock: only the first request rebuilds the cache; others wait
Cache Avalanche
Symptom: A large number of keys expire at the same time, overwhelming the database.
Solutions:
- Add random jitter to TTL values to spread expiration
- Keep critical data permanent with async background refresh
- Circuit breaker and graceful degradation to protect the database
Useful Admin Commands
# View Redis info (memory, connections, persistence, etc.)
INFO
INFO memory
INFO server
# View slow query log
SLOWLOG GET 10
# Clear the current database (use with care)
FLUSHDB
# Clear all databases (use with care)
FLUSHALL
# Check key type
TYPE key
# Delete keys
DEL key1 key2
# Async delete (recommended for large keys — non-blocking)
UNLINK key
# Rename a key
RENAME old-key new-key
# Check memory usage of a key
MEMORY USAGE key
Fonnpo