Redis Best Practices
Core Principles
- •Use Redis for caching, session storage, real-time analytics, and message queuing
- •Choose appropriate data structures for your use case
- •Implement proper key naming conventions and expiration policies
- •Design for high availability and persistence requirements
- •Monitor memory usage and optimize for performance
Key Naming Conventions
- •Use colons as namespace separators
- •Include object type and identifier in key names
- •Keep keys short but descriptive
- •Use consistent naming patterns across your application
code
# Good key naming examples user:1234:profile user:1234:sessions order:5678:items cache:api:products:list queue:email:pending session:abc123def456 rate_limit:api:user:1234
Data Structures
Strings
- •Use for simple key-value storage, counters, and caching
- •Consider using MGET/MSET for batch operations
redis
# Simple caching
SET cache:user:1234 '{"name":"John","email":"john@example.com"}' EX 3600
# Counters
INCR stats:pageviews:homepage
INCRBY stats:downloads:file123 5
# Atomic operations
SETNX lock:resource:456 "owner:abc" EX 30
Hashes
- •Use for objects with multiple fields
- •More memory-efficient than multiple string keys
- •Supports partial updates
redis
# Store user profile HSET user:1234 name "John Doe" email "john@example.com" created_at "2024-01-15" # Get specific fields HGET user:1234 email HMGET user:1234 name email # Increment numeric fields HINCRBY user:1234 login_count 1 # Get all fields HGETALL user:1234
Lists
- •Use for queues, recent items, and activity feeds
- •Consider blocking operations for queue consumers
redis
# Message queue
LPUSH queue:emails '{"to":"user@example.com","subject":"Welcome"}'
RPOP queue:emails
# Blocking pop for workers
BRPOP queue:emails 30
# Recent activity (keep last 100)
LPUSH user:1234:activity "viewed product 567"
LTRIM user:1234:activity 0 99
# Get recent items
LRANGE user:1234:activity 0 9
Sets
- •Use for unique collections, tags, and relationships
- •Supports set operations (union, intersection, difference)
redis
# User tags/interests SADD user:1234:interests "technology" "music" "travel" # Check membership SISMEMBER user:1234:interests "music" # Find common interests SINTER user:1234:interests user:5678:interests # Online users tracking SADD online:users "user:1234" SREM online:users "user:1234" SMEMBERS online:users
Sorted Sets
- •Use for leaderboards, priority queues, and time-series data
- •Elements sorted by score
redis
# Leaderboard ZADD leaderboard:game1 1500 "player:123" 2000 "player:456" 1800 "player:789" # Get top 10 ZREVRANGE leaderboard:game1 0 9 WITHSCORES # Get player rank ZREVRANK leaderboard:game1 "player:123" # Time-based data (score = timestamp) ZADD events:user:1234 1705329600 "login" 1705330000 "purchase" # Get events in time range ZRANGEBYSCORE events:user:1234 1705329600 1705333200
Streams
- •Use for event streaming and log data
- •Supports consumer groups for distributed processing
redis
# Add events to stream XADD events:orders * customer_id 1234 product_id 567 amount 99.99 # Read from stream XREAD COUNT 10 STREAMS events:orders 0 # Consumer groups XGROUP CREATE events:orders order-processors $ MKSTREAM XREADGROUP GROUP order-processors worker1 COUNT 10 STREAMS events:orders > # Acknowledge processed messages XACK events:orders order-processors 1234567890-0
Caching Patterns
Cache-Aside Pattern
python
# Pseudo-code for cache-aside
def get_user(user_id):
# Try cache first
cached = redis.get(f"cache:user:{user_id}")
if cached:
return json.loads(cached)
# Cache miss - fetch from database
user = database.get_user(user_id)
# Store in cache with expiration
redis.setex(f"cache:user:{user_id}", 3600, json.dumps(user))
return user
Write-Through Pattern
python
def update_user(user_id, data):
# Update database
database.update_user(user_id, data)
# Update cache
redis.setex(f"cache:user:{user_id}", 3600, json.dumps(data))
Cache Invalidation
redis
# Delete specific cache DEL cache:user:1234 # Delete by pattern (use with caution in production) # Use SCAN instead of KEYS for large datasets SCAN 0 MATCH cache:user:* COUNT 100 # Tag-based invalidation using sets SADD cache:tags:user:1234 "cache:user:1234:profile" "cache:user:1234:orders" # Invalidate all related caches SMEMBERS cache:tags:user:1234 # Then delete each key
Expiration and Memory Management
TTL Best Practices
- •Always set TTL on cache keys
- •Use jitter to prevent thundering herd
- •Consider sliding expiration for session data
redis
# Set with expiration SET cache:data:123 "value" EX 3600 # Set expiration on existing key EXPIRE cache:data:123 3600 # Check TTL TTL cache:data:123 # Persist key (remove expiration) PERSIST cache:data:123
Memory Management
redis
# Check memory usage INFO memory # Get key memory usage MEMORY USAGE cache:large:object # Configure max memory policy CONFIG SET maxmemory 2gb CONFIG SET maxmemory-policy allkeys-lru
Transactions and Atomicity
MULTI/EXEC Transactions
redis
# Transaction block MULTI INCR stats:views LPUSH recent:views "page:123" EXEC # Watch for optimistic locking WATCH user:1234:balance balance = GET user:1234:balance MULTI SET user:1234:balance (balance - 100) EXEC
Lua Scripts
- •Use for complex atomic operations
- •Scripts execute atomically
lua
-- Rate limiting script
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = tonumber(redis.call('GET', key) or '0')
if current >= limit then
return 0
end
redis.call('INCR', key)
if current == 0 then
redis.call('EXPIRE', key, window)
end
return 1
redis
# Execute Lua script
EVAL "return redis.call('GET', KEYS[1])" 1 mykey
Pub/Sub and Messaging
redis
# Publisher
PUBLISH channel:notifications '{"type":"alert","message":"New order"}'
# Subscriber
SUBSCRIBE channel:notifications
# Pattern subscription
PSUBSCRIBE channel:*
High Availability
Replication
- •Use replicas for read scaling
- •Configure proper persistence on master
redis
# On replica REPLICAOF master_host 6379 # Check replication status INFO replication
Redis Sentinel
- •Use for automatic failover
- •Deploy at least 3 Sentinel instances
Redis Cluster
- •Use for horizontal scaling
- •Data automatically sharded across nodes
- •Use hash tags for related keys
redis
# Hash tags ensure keys go to same slot
SET {user:1234}:profile "data"
SET {user:1234}:settings "data"
Persistence
RDB Snapshots
redis
# Manual snapshot BGSAVE # Configure automatic snapshots CONFIG SET save "900 1 300 10 60 10000"
AOF (Append-Only File)
redis
# Enable AOF CONFIG SET appendonly yes CONFIG SET appendfsync everysec # Rewrite AOF BGREWRITEAOF
Security
- •Require authentication
- •Use TLS for connections
- •Bind to specific interfaces
- •Disable dangerous commands
redis
# Set password CONFIG SET requirepass "your_strong_password" # Authenticate AUTH your_strong_password # Rename dangerous commands (in redis.conf) rename-command FLUSHALL "" rename-command FLUSHDB "" rename-command KEYS ""
Monitoring
redis
# Server info INFO # Memory stats INFO memory # Client connections CLIENT LIST # Slow log SLOWLOG GET 10 # Monitor commands (debug only) MONITOR # Key count per database INFO keyspace
Connection Management
- •Use connection pooling
- •Set appropriate timeouts
- •Handle reconnection gracefully
python
# Python example with connection pool
import redis
pool = redis.ConnectionPool(
host='localhost',
port=6379,
max_connections=50,
socket_timeout=5,
socket_connect_timeout=5
)
redis_client = redis.Redis(connection_pool=pool)
Performance Tips
- •Use pipelining for batch operations
- •Avoid large keys (>100KB values)
- •Use SCAN instead of KEYS in production
- •Monitor and optimize memory usage
- •Consider using RedisJSON for complex JSON operations
redis
# Pipeline example (pseudo-code)
pipe = redis.pipeline()
pipe.get("key1")
pipe.get("key2")
pipe.set("key3", "value")
results = pipe.execute()