Redis Essentials
Quick reference guide for Redis fundamentals.
What is Redis?
Redis is an in-memory data structure store used as:
Key Features:
Installation and Connection
Install Redis
# macOS
brew install redis
# Ubuntu/Debian
sudo apt-get install redis-server
# Start Redis Server
redis-server
# Start Redis CLI
redis-cli
Connect with Node.js
// Using ioredis (recommended)
import Redis from 'ioredis';
const redis = new Redis({
host: 'localhost',
port: 6379,
password: 'your-password', // if authentication enabled
db: 0 // database number
});
// Test connection
redis.ping().then(() => {
console.log('Connected to Redis');
});
Connect with Python
import redis
# Create connection
r = redis.Redis(
host='localhost',
port=6379,
password='your-password',
db=0,
decode_responses=True
)
# Test connection
r.ping()
Basic Commands
Keys and Basic Operations
# ========== Set and Get ==========
SET key value
GET key
# Example
SET user:1:name "Yudi Nugraha"
GET user:1:name
# Returns: "Yudi Nugraha"
# ========== Set with Expiration ==========
SET key value EX seconds
SETEX key seconds value
# Example
SETEX session:abc123 3600 "user_data"
# Expires after 1 hour
# ========== Set if Not Exists ==========
SETNX key value
# ========== Multiple Set/Get ==========
MSET key1 value1 key2 value2
MGET key1 key2
# ========== Check if Key Exists ==========
EXISTS key
# Returns: 1 if exists, 0 if not
# ========== Delete Key ==========
DEL key
# ========== Get All Keys (use with caution in production) ==========
KEYS pattern
# Example: KEYS user:*
# ========== Set Expiration ==========
EXPIRE key seconds
TTL key # Time to live in seconds
PTTL key # Time to live in milliseconds
# ========== Remove Expiration ==========
PERSIST key
# ========== Rename Key ==========
RENAME old_key new_key
Data Types
1. Strings
Simple key-value pairs:
# ========== Set and Get ==========
SET counter 0
GET counter
# Returns: "0"
# ========== Increment/Decrement ==========
INCR counter # Increment by 1
INCRBY counter 5 # Increment by 5
DECR counter # Decrement by 1
DECRBY counter 3 # Decrement by 3
# ========== Append ==========
APPEND key value
# ========== Get String Length ==========
STRLEN key
Node.js Example:
// Set value
await redis.set('user:1:name', 'Yudi Nugraha');
// Get value
const name = await redis.get('user:1:name');
// Set with expiration (60 seconds)
await redis.set('session:abc', 'data', 'EX', 60);
// Increment counter
await redis.incr('page:views');
2. Hashes
Field-value pairs (like objects):
# ========== Set Hash Fields ==========
HSET user:1 name "Yudi" age 30 email "yudi@example.com"
# ========== Get Hash Field ==========
HGET user:1 name
# Returns: "Yudi"
# ========== Get All Hash Fields ==========
HGETALL user:1
# Returns: name "Yudi" age "30" email "yudi@example.com"
# ========== Get Multiple Fields ==========
HMGET user:1 name email
# ========== Check if Field Exists ==========
HEXISTS user:1 name
# ========== Delete Hash Field ==========
HDEL user:1 age
# ========== Get All Field Names ==========
HKEYS user:1
# ========== Get All Values ==========
HVALS user:1
# ========== Increment Hash Field ==========
HINCRBY user:1 age 1
Node.js Example:
// Set hash fields
await redis.hset('user:1', {
name: 'Yudi Nugraha',
email: 'yudi@example.com',
age: 30
});
// Get specific field
const name = await redis.hget('user:1', 'name');
// Get all fields
const user = await redis.hgetall('user:1');
// Returns: { name: 'Yudi Nugraha', email: 'yudi@example.com', age: '30' }
3. Lists
Ordered collections (like arrays):
# ========== Push to List ==========
LPUSH list value # Push to left (beginning)
RPUSH list value # Push to right (end)
# Example
RPUSH tasks "task1" "task2" "task3"
# ========== Pop from List ==========
LPOP list # Pop from left
RPOP list # Pop from right
# ========== Get List Range ==========
LRANGE list start stop
LRANGE tasks 0 -1 # Get all items
# ========== Get List Length ==========
LLEN list
# ========== Get Element by Index ==========
LINDEX list index
# ========== Set Element by Index ==========
LSET list index value
# ========== Remove Elements ==========
LREM list count value
Node.js Example:
// Add items to queue
await redis.rpush('queue:tasks', 'task1', 'task2', 'task3');
// Get all items
const tasks = await redis.lrange('queue:tasks', 0, -1);
// Returns: ['task1', 'task2', 'task3']
// Process queue (FIFO)
const task = await redis.lpop('queue:tasks');
4. Sets
Unordered unique collections:
# ========== Add to Set ==========
SADD set member1 member2
# Example
SADD tags:post:1 "javascript" "nodejs" "redis"
# ========== Get All Members ==========
SMEMBERS tags:post:1
# ========== Check if Member Exists ==========
SISMEMBER tags:post:1 "javascript"
# Returns: 1 if exists, 0 if not
# ========== Remove from Set ==========
SREM set member
# ========== Get Set Size ==========
SCARD set
# ========== Random Member ==========
SRANDMEMBER set
# ========== Pop Random Member ==========
SPOP set
# ========== Set Operations ==========
SUNION set1 set2 # Union
SINTER set1 set2 # Intersection
SDIFF set1 set2 # Difference
Node.js Example:
// Add tags
await redis.sadd('tags:post:1', 'javascript', 'nodejs', 'redis');
// Check if tag exists
const hasTag = await redis.sismember('tags:post:1', 'javascript');
// Returns: 1
// Get all tags
const tags = await redis.smembers('tags:post:1');
// Returns: ['javascript', 'nodejs', 'redis']
// Get common tags between posts
const commonTags = await redis.sinter('tags:post:1', 'tags:post:2');
5. Sorted Sets
Ordered unique collections with scores:
# ========== Add to Sorted Set ==========
ZADD sortedset score member
# Example (Leaderboard)
ZADD leaderboard 100 "Alice"
ZADD leaderboard 85 "Bob"
ZADD leaderboard 92 "Charlie"
# ========== Get Range by Rank ==========
ZRANGE leaderboard 0 -1 # Ascending
ZREVRANGE leaderboard 0 -1 # Descending (highest first)
# ========== Get Range with Scores ==========
ZRANGE leaderboard 0 -1 WITHSCORES
# ========== Get Range by Score ==========
ZRANGEBYSCORE leaderboard 80 100
# ========== Get Member Score ==========
ZSCORE leaderboard "Alice"
# ========== Get Member Rank ==========
ZRANK leaderboard "Alice" # Ascending rank
ZREVRANK leaderboard "Alice" # Descending rank
# ========== Increment Score ==========
ZINCRBY leaderboard 10 "Alice"
# ========== Get Set Size ==========
ZCARD leaderboard
# ========== Remove Member ==========
ZREM leaderboard "Bob"
Node.js Example:
// Add scores
await redis.zadd('leaderboard', 100, 'Alice', 85, 'Bob', 92, 'Charlie');
// Get top 10 players (highest scores)
const topPlayers = await redis.zrevrange('leaderboard', 0, 9, 'WITHSCORES');
// Returns: ['Alice', '100', 'Charlie', '92', 'Bob', '85']
// Get player rank
const rank = await redis.zrevrank('leaderboard', 'Alice');
// Returns: 0 (highest rank)
// Increment score
await redis.zincrby('leaderboard', 5, 'Alice');
Caching Patterns
Cache-Aside (Lazy Loading)
async function getUser(userId) {
const cacheKey = `user:${userId}`;
// Try to get from cache
let user = await redis.get(cacheKey);
if (user) {
// Cache hit
return JSON.parse(user);
}
// Cache miss - fetch from database
user = await db.findUser(userId);
// Store in cache with 1 hour expiration
await redis.set(cacheKey, JSON.stringify(user), 'EX', 3600);
return user;
}
Write-Through
async function updateUser(userId, data) {
const cacheKey = `user:${userId}`;
// Update database
const user = await db.updateUser(userId, data);
// Update cache
await redis.set(cacheKey, JSON.stringify(user), 'EX', 3600);
return user;
}
Write-Behind (Write-Back)
async function updateUserScore(userId, score) {
const cacheKey = `user:${userId}:score`;
// Update cache immediately
await redis.set(cacheKey, score);
// Queue database update (async)
await queue.add('updateScore', { userId, score });
return score;
}
Cache Invalidation
async function deleteUser(userId) {
// Delete from database
await db.deleteUser(userId);
// Invalidate cache
await redis.del(`user:${userId}`);
}
// Pattern-based invalidation
async function invalidateUserCache(userId) {
// Delete all keys matching pattern
const keys = await redis.keys(`user:${userId}:*`);
if (keys.length > 0) {
await redis.del(...keys);
}
}
Session Management
// Store session
async function createSession(userId, data) {
const sessionId = generateId();
const sessionKey = `session:${sessionId}`;
await redis.hset(sessionKey, {
userId,
createdAt: Date.now(),
...data
});
// Set expiration (24 hours)
await redis.expire(sessionKey, 86400);
return sessionId;
}
// Get session
async function getSession(sessionId) {
const sessionKey = `session:${sessionId}`;
return await redis.hgetall(sessionKey);
}
// Extend session
async function extendSession(sessionId) {
const sessionKey = `session:${sessionId}`;
await redis.expire(sessionKey, 86400);
}
// Delete session
async function deleteSession(sessionId) {
const sessionKey = `session:${sessionId}`;
await redis.del(sessionKey);
}
Rate Limiting
Fixed Window
async function isRateLimited(userId, limit = 100, window = 60) {
const key = `rate:${userId}:${Math.floor(Date.now() / 1000 / window)}`;
const current = await redis.incr(key);
if (current === 1) {
// First request in window, set expiration
await redis.expire(key, window);
}
return current > limit;
}
Sliding Window
async function isRateLimitedSlidingWindow(userId, limit = 100, window = 60) {
const key = `rate:${userId}`;
const now = Date.now();
const windowStart = now - (window * 1000);
// Remove old entries
await redis.zremrangebyscore(key, 0, windowStart);
// Count requests in window
const count = await redis.zcard(key);
if (count < limit) {
// Add current request
await redis.zadd(key, now, `${now}`);
await redis.expire(key, window);
return false;
}
return true;
}
Pub/Sub (Messaging)
Publisher
// Publish message
await redis.publish('notifications', JSON.stringify({
type: 'user_registered',
userId: 123,
timestamp: Date.now()
}));
Subscriber
const subscriber = new Redis();
subscriber.subscribe('notifications', (err, count) => {
console.log(`Subscribed to ${count} channel(s)`);
});
subscriber.on('message', (channel, message) => {
console.log(`Received message from ${channel}:`, message);
const data = JSON.parse(message);
// Handle notification
});
Transactions
Execute multiple commands atomically:
// Using MULTI/EXEC
const multi = redis.multi();
multi.set('key1', 'value1');
multi.set('key2', 'value2');
multi.incr('counter');
const results = await multi.exec();
// All commands executed atomically
// Watch keys for changes
await redis.watch('balance:1');
const balance = await redis.get('balance:1');
const multi = redis.multi();
multi.set('balance:1', balance - 100);
multi.set('balance:2', balance + 100);
try {
await multi.exec();
// Transaction succeeded
} catch (error) {
// Transaction failed (key was modified)
}
Lua Scripts
Execute complex operations atomically:
// Rate limiting script
const script = `
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = redis.call('INCR', key)
if current == 1 then
redis.call('EXPIRE', key, window)
end
if current > limit then
return 0
end
return 1
`;
const allowed = await redis.eval(
script,
1,
`rate:user:123`,
100,
60
);
if (allowed) {
// Request allowed
} else {
// Rate limited
}
Best Practices
Naming Conventions
// Use colons to create hierarchy
user:1234:profile
user:1234:sessions
order:5678:items
// Use descriptive names
cache:product:1234
session:abc123def
rate:user:5678:api
Memory Management
// Set expiration on keys
await redis.set('cache:key', value, 'EX', 3600);
// Use appropriate data types
// Hash for objects (more memory efficient than JSON string)
await redis.hset('user:1', { name: 'Yudi', age: 30 });
// Monitor memory usage
const info = await redis.info('memory');
Error Handling
try {
await redis.get('key');
} catch (error) {
console.error('Redis error:', error);
// Fallback to database or return error
}
// Connection error handling
redis.on('error', (error) => {
console.error('Redis connection error:', error);
});
redis.on('connect', () => {
console.log('Connected to Redis');
});
Common Use Cases
1. Session Store
// Express.js with Redis session
import session from 'express-session';
import RedisStore from 'connect-redis';
app.use(session({
store: new RedisStore({ client: redis }),
secret: 'your-secret',
resave: false,
saveUninitialized: false,
cookie: { maxAge: 86400000 } // 24 hours
}));
2. Job Queue
// Using Bull (Redis-based queue)
import Bull from 'bull';
const emailQueue = new Bull('email', {
redis: { host: 'localhost', port: 6379 }
});
// Add job
await emailQueue.add({ to: 'user@example.com', subject: 'Welcome' });
// Process jobs
emailQueue.process(async (job) => {
await sendEmail(job.data);
});
3. Real-time Leaderboard
// Add score
await redis.zadd('leaderboard:daily', score, userId);
// Get top 10
const top10 = await redis.zrevrange('leaderboard:daily', 0, 9, 'WITHSCORES');
// Get user rank
const rank = await redis.zrevrank('leaderboard:daily', userId);
4. Distributed Lock
async function acquireLock(lockKey, timeout = 10) {
const lockValue = generateId();
const acquired = await redis.set(
lockKey,
lockValue,
'EX',
timeout,
'NX'
);
return acquired ? lockValue : null;
}
async function releaseLock(lockKey, lockValue) {
const script = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`;
return await redis.eval(script, 1, lockKey, lockValue);
}