Redis data structures, commands, expiry, pub/sub, and common caching patterns
Store and retrieve string values
SET key "value" # set a key
SET key "value" EX 3600 # set with 1 hour TTL
GET key # get value
GETSET key "newval" # get old, set new (atomic)
SETNX key "value" # set only if not exists
MSET a 1 b 2 c 3 # set multiple keys
MGET a b c # get multiple keysModify numeric and string values atomically
INCR counter # increment by 1
INCRBY counter 10 # increment by N
DECR counter # decrement by 1
DECRBY counter 5 # decrement by N
APPEND key " more text" # append to stringAdd and remove elements from list ends
LPUSH mylist "a" # push to left (head)
RPUSH mylist "b" # push to right (tail)
LPOP mylist # pop from left
RPOP mylist # pop from right
BLPOP mylist 30 # blocking pop (wait up to 30s)Read list elements and length
LRANGE mylist 0 -1 # get all elements
LRANGE mylist 0 9 # get first 10
LLEN mylist # length of list
LINDEX mylist 0 # element at index
LSET mylist 0 "newval" # set element at indexStore and retrieve hash field values
HSET user:1 name "Alice" age 30
HGET user:1 name # "Alice"
HMGET user:1 name age # ["Alice", "30"]
HGETALL user:1 # all fields and valuesUpdate, delete, and inspect hash fields
HDEL user:1 age # delete field
HEXISTS user:1 name # 1 (exists)
HKEYS user:1 # ["name", "age"]
HVALS user:1 # ["Alice", "30"]
HLEN user:1 # number of fields
HINCRBY user:1 score 5 # increment numeric fieldAdd members and query set membership
SADD myset "a" "b" "c" # add members
SMEMBERS myset # all members
SISMEMBER myset "a" # 1 (is member)
SCARD myset # count members
SREM myset "b" # remove member
SRANDMEMBER myset # random memberUnion, intersection, and difference of sets
SUNION set1 set2 # union
SINTER set1 set2 # intersection
SDIFF set1 set2 # difference (in set1 not set2)
SUNIONSTORE dest set1 set2 # store result in destAdd scored members and query by rank
ZADD leaderboard 1000 "alice"
ZADD leaderboard 850 "bob" 920 "carol"
ZRANGE leaderboard 0 -1 # all by rank (ascending)
ZRANGE leaderboard 0 -1 WITHSCORES # with scores
ZREVRANGE leaderboard 0 9 # top 10 (descending)
ZRANK leaderboard "alice" # rank (0-indexed)
ZSCORE leaderboard "alice" # scoreSet and check expiration on keys
EXPIRE key 3600 # expire in 3600 seconds
EXPIREAT key 1893456000 # expire at Unix timestamp
TTL key # seconds remaining (-1 = no TTL, -2 = gone)
PERSIST key # remove expiryCheck and manage keys
EXISTS key # 1 if exists
DEL key1 key2 # delete keys
TYPE key # "string", "list", "hash", etc.
SCAN 0 MATCH user:* COUNT 100 # safe iteration (use instead of KEYS)Message passing between clients
# Client 1 - subscribe
SUBSCRIBE channel1
PSUBSCRIBE news.* # pattern subscribe
# Client 2 - publish
PUBLISH channel1 "message"
# Client 1 - unsubscribe
UNSUBSCRIBE channel1Store user sessions with automatic expiry
# Store session (Node.js with ioredis)
const sessionId = crypto.randomUUID();
const sessionData = JSON.stringify({ userId: 123, role: "admin" });
await redis.set(`session:${sessionId}`, sessionData, "EX", 86400);
// EX 86400 = expire in 24 hours
# Read session
const raw = await redis.get(`session:${sessionId}`);
const session = raw ? JSON.parse(raw) : null;
# Refresh session on activity
await redis.expire(`session:${sessionId}`, 86400);
# Delete on logout
await redis.del(`session:${sessionId}`);Simple sliding window rate limiter
# Rate limit: 100 requests per minute per IP
async function isRateLimited(ip: string): Promise<boolean> {
const key = `rate:${ip}:${Math.floor(Date.now() / 60000)}`;
const count = await redis.incr(key);
if (count === 1) {
await redis.expire(key, 60); // set TTL only on first increment
}
return count > 100; // limit: 100 req/min
}
# Usage
if (await isRateLimited(clientIp)) {
return res.status(429).json({ error: "Too many requests" });
}Real-time leaderboard using ZADD and ZREVRANGE
# Update score
await redis.zadd("leaderboard", score, userId);
# Get top 10 with scores
const top10 = await redis.zrevrange("leaderboard", 0, 9, "WITHSCORES");
// Returns flat array: [userId, score, userId, score, ...]
# Get rank of a user (0 = top)
const rank = await redis.zrevrank("leaderboard", userId);
# Get score of a user
const score = await redis.zscore("leaderboard", userId);
# Get users in a score range
const players = await redis.zrangebyscore("leaderboard", 900, 1000);Always set a TTL on cached keys — never cache without an expiry to prevent unbounded memory growth
Use SCAN instead of KEYS pattern in production — KEYS blocks the server
Use namespaced key prefixes (user:123:session) to organize and avoid collisions
Use pipelining (batch commands) for multiple operations to reduce round-trip latency
Monitor memory usage with INFO memory and set maxmemory-policy allkeys-lru for cache use cases