Redis Complete Guide in 2026
Published: 2026-05-04
Redis is the world's most popular in-memory data store, essential for caching, session management, real-time analytics, and message queuing. This comprehensive guide covers Redis data types, persistence, clustering, sentinel, Lua scripting, pub/sub, streams, security, performance tuning, and production deployment best practices.
Introduction to Redis
What is Redis?
┌─────────────────────────────────────────────────────────┐
│ Redis (Remote Dictionary Server) │
│ │
│ • In-memory data structure store │
│ • Key-value database │
│ • Cache, message broker, and queue │
│ • Sub-millisecond response times │
│ • Supports data structures: strings, lists, │
│ sets, sorted sets, hashes, bitmaps, hyperloglogs, │
│ geospatial indexes, streams │
│ │
│ Common Use Cases: │
│ • Caching (API responses, database queries) │
│ • Session storage │
│ • Real-time analytics │
│ • Rate limiting │
│ • Message queues │
│ • Distributed locks │
└─────────────────────────────────────────────────────────┘
Installation
brew install redis
brew services start redis
# Ubuntu/Debian
sudo apt update
sudo apt install redis-server
docker run -d --name redis -p 6379:6379 redis:latest
# Redis with persistence
docker run -d --name redis \
-p 6379:6379 \
-v redis-data:/data \
redis:latest \
redis-server --appendonly yes
redis-cli ping # Should return PONG
Data Types
Strings
# Basic string operations
SET user:123:name "Alice"
GET user:123:name
# With expiry (seconds)
SET session:abc123 "data" EX 3600
# With expiry (milliseconds)
SET token:xyz PX 3000
# Get remaining TTL
TTL session:abc123
# Set if not exists (atomic)
SETNX lock:resource:1 "locked"
# Multiple values
MSET user:1:name "Alice" user:1:email "alice@example.com" user:1:age "30"
MGET user:1:name user:1:email
# Increment/decrement
INCR pageviews:home
INCRBY pageviews:home 10
DECR counter
INCRBYFLOAT price:product:1 0.99
# String length
STRLEN user:1:name
# Get range
GETRANGE user:1:bio 0 10
# Set range
SETRANGE user:1:bio 0 "Hello"
Lists
LPUSH notifications "New order received"
RPUSH notifications "Order shipped"
LPOP notifications
RPOP notifications
# Blocking pop (for queues)
BLPOP notifications 0 # Wait forever
BRPOP notifications 5 # Wait 5 seconds
# List length
LLEN notifications
LRANGE notifications 0 9
# Trim (keep only first 100)
LTRIM notifications 0 99
LINSERT notifications BEFORE "Order shipped" "Order confirmed"
# Index access
LINDEX notifications 0
Sets
# Add/remove/members
SADD tags:article:1 "python" "redis" "database"
SREM tags:article:1 "database"
SMEMBERS tags:article:1
# Check membership
SISMEMBER tags:article:1 "python"
SCARD tags:article:1
# Random members
SRANDMEMBER tags:article:1 3 # 3 random without removing
SPOP tags:article:1 2 # 2 random AND remove
# Set operations
SADD set:a 1 2 3 4
SADD set:b 3 4 5 6
SINTER set:a set:b # Intersection: 3, 4
SUNION set:a set:b # Union: 1,2,3,4,5,6
SDIFF set:a set:b # Difference: 1, 2
# Store results
SINTERSTORE set:intersection set:a set:b
Sorted Sets
# Add with score (for leaderboards)
ZADD leaderboard 1000 "alice"
ZADD leaderboard 950 "bob"
ZADD leaderboard 1100 "charlie"
# Get rank (0 = highest)
ZREVRANK leaderboard "alice" # 1 (charlie is 0)
# Get score
ZSCORE leaderboard "alice" # 1000
ZREVRANGE leaderboard 0 9 WITHSCORES
# Bottom 10
ZRANGE leaderboard 0 9 WITHSCORES
# Score range
ZRANGEBYSCORE leaderboard 900 1000
# Count in range
ZCOUNT leaderboard 900 1000
# Increment score
ZINCRBY leaderboard 50 "alice" # Now 1050
# Remove by rank
ZREMRANGEBYRANK leaderboard 0 9 # Remove bottom 10
Hashes
# Hash operations
HSET user:123 name "Alice" email "alice@example.com" age "30"
HGET user:123 name
HGETALL user:123
HMGET user:123 name email
HMSET user:456 name "Bob" email "bob@example.com"
HINCRBY user:123 age 1 # Increment age
HEXISTS user:123 email # Check if field exists
HDEL user:123 age # Delete field
HLEN user:123 # Number of fields
HKEYS user:123 # All field names
HVALS user:123 # All values
Bitmaps
# Bitmap for user activity tracking
SETBIT user:123:login:2024 0 1 # Day 0 (Jan 1) - user logged in
SETBIT user:123:login:2024 5 1 # Day 5 (Jan 6)
# Check if active on day 5
GETBIT user:123:login:2024 5
# Count active days
BITCOUNT user:123:login:2024
# Find first set bit
BITPOS user:123:login:2024 1 # First day with login
# Bitwise operations
BITOP AND user:and:123 user:123:login:2024 user:456:login:2024
HyperLogLog
# HyperLogLog for approximate unique counts
PFADD visitors:daily "user:123" "user:456" "user:789"
PFADD visitors:daily "user:123" "user:456" # Duplicate - no effect
PFCOUNT visitors:daily # Approximate unique count
# Merge multiple days
PFMERGE visitors:week visitors:daily:2024-01-01 visitors:daily:2024-01-02
PFCOUNT visitors:week
Geospatial
# Add locations
GEOADD locations -122.4194 37.7749 "san-francisco"
GEOADD locations -122.2711 37.8044 "oakland"
GEOADD locations -121.8863 37.3382 "san-jose"
# Distance between locations
GEODIST locations "san-francisco" "san-jose" km
# Find locations within radius
GEORADIUS locations -122.4194 37.7749 50 km WITHDIST ASC COUNT 10
# Get position
GEOPOS locations "san-francisco"
# Find city containing point
GEOSEARCH locations FROMLONLAT -122.4194 37.7749 BYRADIUS 50 km
Persistence
RDB Snapshots
# redis.conf
save 900 1 # After 1 change in 900 seconds
save 300 100 # After 100 changes in 300 seconds
save 60 10000 # After 10000 changes in 60 seconds
# Disable RDB (if using AOF)
# Snapshot file location
dbfilename dump.rdb
dir /var/lib/redis
# Manual save
BGSAVE # Background save
LASTSAVE # Timestamp of last successful save
# Check if saving in progress
INFO persistence
AOF (Append-Only File)
# redis.conf
appendonly yes
appendfilename "appendonly.aof"
# fsync policy
appendfsync always # Every write (slow, most durable)
appendfsync everysec # Every second (default, balanced)
appendfsync no # Let OS decide (fastest, risky)
# AOF rewrite (compact)
BGREWRITEAOF
# Check AOF
redis-cli info | grep aof
Hybrid Persistence (Redis 7.0+)
# redis.conf - Redis 7.0+ multi-part AOF
aof-use-rdb-preamble yes # Use RDB for base, AOF for增量
Lua Scripting
Basic Scripts
-- Simple Lua script (atomic execution)
-- KEYS[1] = user key
-- ARGV[1] = increment amount
local current = redis.call('GET', KEYS[1])
if current == false then
current = 0
current = tonumber(current)
local increment = tonumber(ARGV[1])
local new_value = current + increment
redis.call('SET', KEYS[1], new_value)
return new_value
-- Usage with redis-cli
-- redis-cli --eval increment.lua user:123:balance , 100
-- Distributed lock in Lua
-- KEYS[1] = lock key
-- ARGV[1] = lock value (unique identifier)
-- ARGV[2] = TTL in milliseconds
local lock = redis.call('SET', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2])
if lock == 'OK' then
-- Rate limiter (sliding window)
-- KEYS[1] = rate limit key
-- ARGV[1] = window size in ms
-- ARGV[2] = max requests per window
local key = KEYS[1]
local window = tonumber(ARGV[1])
local limit = tonumber(ARGV[2])
local now = redis.call('TIME')[1]
local window_start = now - window
-- Remove old entries
redis.call('ZREMRANGEBYSCORE', key, '-inf', window_start)
-- Count current requests
local count = redis.call('ZCARD', key)
if count >= limit then
redis.call('ZADD', key, now, now .. '-' .. math.random())
redis.call('PEXPIRE', key, window)
Script Management
# Load script once, use hash
SCRIPT LOAD "return redis.call('GET', KEYS[1])"
# Returns script hash: "a4203f..."
# Execute by hash
EVALSHA "a4203f..." 1 user:123
# Check if script exists
SCRIPT EXISTS "a4203f..."
# Flush script cache (rarely needed)
SCRIPT FLUSH
Pub/Sub
Basic Pub/Sub
# Terminal 1 - Subscribe to channel
SUBSCRIBE notifications
# OR multiple channels
PSUBSCRIBE notifications.*
# Terminal 2 - Publish
PUBLISH notifications "Hello, subscribers!"
PUBLISH notifications:urgent "Critical alert!"
Pattern Subscription
-- Lua script for reliable queue using Pub/Sub
-- Publisher
redis.call('PUBLISH', channel, message)
-- Subscriber with acknowledgment
while true do
local msg = redis.call('SUBSCRIBE', channel)
-- Process message
-- ACK only after successful processing
Redis Streams (for Reliable Messaging)
# Add to stream
XADD mystream * field1 value1 field2 value2
# * = auto-generate ID
# Read from beginning
XRANGE mystream - +
# Read new messages
XREAD STREAMS mystream $
# Consumer groups (for distributed processing)
XGROUP CREATE mystream mygroup $ MKSTREAM
# Read as consumer in group
XREADGROUP GROUP mygroup consumer1 STREAMS mystream ">"
# Acknowledge message
XACK mystream mygroup 1526564898035-0
# Pending messages (unacknowledged)
XPENDING mystream mygroup
# Claim pending messages
XCLAIM mystream mygroup consumer2 0 1526564898035-0
Clustering
Cluster Configuration
# redis.conf for cluster node
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 15000
cluster-replica-validity-factor 2
# Minimum cluster size
cluster-require-full-coverage yes
cluster-migration-barrier 1
Creating a Cluster
# Create cluster (redis-cli >= 7.0)
redis-cli --cluster create \
10.0.0.1:6379 \
10.0.0.2:6379 \
10.0.0.3:6379 \
10.0.0.4:6379 \
10.0.0.5:6379 \
10.0.0.6:6379 \
--cluster-replicas 1
# 3 masters + 3 slaves (1 replica each)
# Check cluster info
redis-cli -c -h 10.0.0.1 cluster info
# List nodes
redis-cli -c -h 10.0.0.1 cluster nodes
Cluster Commands
# Connect to cluster
redis-cli -c -h 10.0.0.1
# Auto-rebalance slots
redis-cli --cluster rebalance 10.0.0.1:6379
# Add new node
redis-cli --cluster add-node 10.0.0.7:6379 10.0.0.1:6379
# Add replica
redis-cli --cluster add-node 10.0.0.8:6379 10.0.0.1:6379 --cluster-slave --cluster-master-id <node-id>
# Remove node
redis-cli --cluster del-node 10.0.0.1:6379 <node-id>
# Reshard slots
redis-cli --cluster reshard 10.0.0.1:6379
Key Hash Tags (for Multi-Key Operations)
# Force keys to same slot (for transactions, sorted sets)
# Use braces: {user:123}:cart
# All {user:123}:* keys go to same slot
SET {user:123}:cart "item1,item2"
SET {user:123}:wishlist "item3,item4"
# Now MGET across multiple keys is possible (same slot)
MGET {user:123}:cart {user:123}:wishlist
Sentinel (High Availability)
Sentinel Configuration
# sentinel.conf
sentinel monitor mymaster 10.0.0.1 6379 2 # Quorum: 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
# Auth for master
sentinel auth-pass mymaster mypassword
# Start sentinel
redis-sentinel /path/to/sentinel.conf
# Or with redis-server
redis-server /path/to/sentinel.conf --sentinel
Sentinel Commands
# Check sentinel info
redis-cli -p 26379 INFO
# List monitored masters
redis-cli -p 26379 SENTINEL masters
# Get master details
redis-cli -p 26379 SENTINEL master mymaster
# Get master IP (for connection)
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
# Force failover
redis-cli -p 26379 SENTINEL failover mymaster
# Check for replica lag
redis-cli -p 26379 SENTINEL replicas mymaster
Application Connection
// Node.js with Sentinel
const Redis = require('ioredis');
const RedisSentinel = require('ioredis-sentinel');
const sentinel = new RedisSentinel(['10.0.0.1:26379', '10.0.0.2:26379']);
const redis = new Redis({
sentinels: [{ host: '10.0.0.1', port: 26379 }],
name: 'mymaster',
password: 'mypassword',
enableReadyCheck: true,
connectTimeout: 10000
redis.on('error', (err) => {
console.error('Redis error:', err);
Security
Authentication
# redis.conf
requirepass mystrongpassword
# Or via command
CONFIG SET requirepass "mystrongpassword"
AUTH mystrongpassword
# For specific commands
# ACL (Redis 6.0+)
ACL LIST # List all ACL rules
ACL SETUSER alice +GET +SET ~items:* >password123
ACL SETUSER bob +GET ~items:read:* -@all
TLS
# redis.conf
tls-port 6380
port 0 # Disable non-TLS
tls-cert-file /path/to/redis.crt
tls-key-file /path/to/redis.key
tls-ca-cert-file /path/to/ca.crt
# Require TLS
tls-auth-clients no # Skip client cert validation
tls-auth-clients yes # Require client cert
Network Security
# Bind to specific IP
bind 127.0.0.1 10.0.0.1
# Disable dangerous commands
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command DEBUG ""
rename-command CONFIG "CONFIG_9sd8f7s"
# Set max memory
maxmemory 2gb
maxmemory-policy allkeys-lru
Performance Tuning
Memory Optimization
# Memory usage info
redis-cli INFO memory
redis-cli MEMORY STATS
# Check big keys
redis-cli --bigkeys
# Find memory usage of key
redis-cli DEBUG OBJECT ENCODING user:123
redis-cli MEMORY USAGE user:123
# Eviction policies
# noeviction: Return error when OOM
# allkeys-lru: Remove least recently used
# allkeys-lfu: Remove least frequently used
# volatile-lru: LRU only for keys with TTL
# volatile-lfu: LFU only for keys with TTL
# allkeys-random: Random removal
# volatile-random: Random with TTL
# volatile-ttl: Remove keys with shortest TTL
maxmemory-policy allkeys-lru
Pipeline and Transactions
# Pipeline (batch commands)
redis-cli --pipe <<EOF
SET key1 value1
SET key2 value2
INCR counter
# Or with redis-py
pipe = redis.pipeline()
pipe.set('key1', 'value1')
pipe.set('key2', 'value2')
pipe.incr('counter')
results = pipe.execute()
# Transactions (MULTI/EXEC)
SET key1 value1
INCR counter
# WATCH for optimistic locking
WATCH user:123:balance
balance = GET user:123:balance
# ... check balance ...
SET user:123:balance $new_balance
EXEC # Fails if key changed
Slow Queries
# Enable slow log
slowlog-log-slower-than 10000 # 10ms in microseconds
slowlog-max-len 128 # Keep last 128 entries
# View slow queries
SLOWLOG GET 10
# Current configuration
SLOWLOG RESET
Caching Patterns
Cache-Aside
async function getUser(userId) {
// Try cache first
const cached = await redis.get(`user:${userId}`);
if (cached) {
return JSON.parse(cached);
// Cache miss - fetch from DB
const user = await db.getUser(userId);
// Store in cache with TTL
await redis.setex(`user:${userId}`, 3600, JSON.stringify(user));
return user;
async function updateUser(userId, data) {
// Update DB
await db.updateUser(userId, data);
// Invalidate cache
await redis.del(`user:${userId}`);
Write-Through
async function createUser(userId, data) {
// Write to both DB and cache simultaneously
await Promise.all([
db.createUser(userId, data),
redis.setex(`user:${userId}`, 3600, JSON.stringify(data))
return data;
Distributed Lock
async function acquireLock(key, ttlMs = 30000) {
const lockValue = uuid.v4();
const acquired = await redis.set(key, lockValue, 'PX', ttlMs, 'NX');
if (acquired === 'OK') {
return lockValue;
return null;
async function releaseLock(key, lockValue) {
// Lua script for atomic check-and-delete
const script = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
await redis.eval(script, 1, key, lockValue);
Rate Limiter
async function checkRateLimit(userId, limit = 100, windowSecs = 60) {
const key = `ratelimit:${userId}`;
const now = Date.now();
const windowStart = now - (windowSecs * 1000);
const multi = redis.multi();
multi.zremrangebyscore(key, '-inf', windowStart);
multi.zadd(key, now, `${now}`);
multi.zcard(key);
multi.expire(key, windowSecs);
const results = await multi.exec();
const requestCount = results[2];
allowed: requestCount <= limit,
remaining: Math.max(0, limit - requestCount),
resetAt: now + (windowSecs * 1000)
Real-World Patterns
Session Store
// Express session with Redis
const session = require('express-session');
const RedisStore = require('connect-redis').default;
const { createClient } = require('redis');
const redisClient = createClient({
url: process.env.REDIS_URL
await redisClient.connect();
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET,
name: 'sessionId',
resave: false,
saveUninitialized: false,
secure: true,
httpOnly: true,
maxAge: 7 * 24 * 60 * 60 * 1000 // 7 days
Job Queue
// Simple job queue with Redis
async function enqueueJob(queueName, jobData) {
const jobId = uuid.v4();
const job = {
data: jobData,
status: 'pending',
createdAt: Date.now()
await redis.hset(`job:${jobId}`, job);
await redis.rpush(`queue:${queueName}`, jobId);
return jobId;
async function dequeueJob(queueName) {
const jobId = await redis.lpop(`queue:${queueName}`);
if (!jobId) return null;
const job = await redis.hgetall(`job:${jobId}`);
job.status = 'processing';
await redis.hset(`job:${jobId}`, 'status', 'processing');
return { jobId, job };
async function completeJob(jobId, result) {
await redis.hset(`job:${jobId}`, {
status: 'completed',
completedAt: Date.now(),
result: JSON.stringify(result)
async function failJob(jobId, error) {
await redis.hset(`job:${jobId}`, {
status: 'failed',
failedAt: Date.now(),
error: error.message
Leaderboard
// Game leaderboard
async function submitScore(userId, gameId, score) {
const key = `leaderboard:${gameId}`;
await redis.zadd(key, score, `${userId}`);
async function getTopScores(gameId, count = 10) {
const key = `leaderboard:${gameId}`;
return redis.zrevrange(key, 0, count - 1, 'WITHSCORES');
async function getUserRank(userId, gameId) {
const key = `leaderboard:${gameId}`;
const rank = await redis.zrevrank(key, `${userId}`);
const score = await redis.zscore(key, `${userId}`);
return { rank: rank + 1, score };
async function getAroundMe(userId, gameId, range = 5) {
const key = `leaderboard:${gameId}`;
const rank = await redis.zrevrank(key, `${userId}`);
const start = Math.max(0, rank - range);
const end = rank + range;
const results = await redis.zrevrange(key, start, end, 'WITHSCORES');
return results;
Checklist
[ ] Install and configure Redis properly
[ ] Use appropriate data types for your use case
[ ] Enable persistence (RDB + AOF recommended)
[ ] Set up authentication and TLS in production
[ ] Configure memory eviction policy
[ ] Use connection pooling in applications
[ ] Implement cache-aside pattern with TTL
[ ] Use pipelining for batch operations
[ ] Implement distributed locks for critical sections
[ ] Set up Redis Sentinel for high availability
[ ] Use Redis Cluster for horizontal scaling
[ ] Monitor memory usage and slow queries
[ ] Use HyperLogLog for approximate unique counts
[ ] Implement rate limiting with sorted sets
[ ] Use streams for reliable message queuing
[ ] Configure proper key expiration
[ ] Monitor and tune slow log threshold
[ ] Back up RDB snapshots regularly
[ ] Document key naming conventions
[ ] Test failover and recovery procedures
Conclusion
Redis is essential for modern application architecture:
Data Types — Strings, Lists, Sets, Sorted Sets, Hashes, Bitmaps, Geospatial
Persistence — RDB snapshots, AOF, hybrid mode
Lua Scripting — Atomic operations, complex logic
Pub/Sub — Real-time messaging, notifications
Streams — Reliable message queues, consumer groups
Clustering — Horizontal scaling, sharding
Sentinel — High availability, automatic failover
Security — Authentication, TLS, ACL
Performance — Pipelining, memory optimization
Patterns — Caching, locks, rate limiting, sessions
Redis powers the fastest applications in the world.
Rating: 5/5 for Redis complete guide.
This article is for educational purposes.
Categories: Redis, Database, Caching, NoSQL, In-Memory, Performance, Distributed Systems, Python, Node.js
Tags: Redis tutorial, Redis data structures, caching, Redis cluster, pub/sub, Lua scripting, Redis performance
This article contains affiliate links. If you sign up through the links above, I may earn a commission at no additional cost to you.
Ready to Build Your AI Business?
Get started with Systeme.io for free — All-in-one platform for building your online business with AI tools.
Top comments (0)