Lists
Redis lists are linked-list-backed sequences of strings. LPUSH / RPUSH append, LPOP / RPOP remove, BLPOP blocks — everything you need for queues, stacks, and recent-N caches.
LPUSH/RPUSH, LRANGE, BLPOP, queues
EXAMPLE
# 1) Basics — left / right append + pop
RPUSH mylist a b c # list = [a, b, c]
LPUSH mylist z # list = [z, a, b, c]
LPOP mylist # z (returns + removes from head)
RPOP mylist # c (from tail)
# 2) Read
LRANGE mylist 0 -1 # all elements (0 to last)
LRANGE mylist 0 9 # first 10
LRANGE mylist -10 -1 # last 10
LLEN mylist # length
LINDEX mylist 0 # element at index
# 3) Modify by index / value
LSET mylist 0 'new-head'
LINSERT mylist BEFORE 'b' 'between'
LREM mylist 2 'foo' # remove first 2 'foo' (negative count = from tail)
# 4) Trim — keep only first / last N (great for activity feeds)
LTRIM feed:user:42 0 99 # keep first 100
# 5) Blocking pop — wait for an item (for queue workers)
BLPOP queue:jobs 5 # wait up to 5 sec for an item
BRPOPLPUSH queue:jobs queue:in-flight 0 # atomic move (legacy)
BLMOVE queue:jobs queue:in-flight LEFT RIGHT 0 # modern, atomic
# 6) Right-pop + push to a second list — "reliable queue" pattern
# 1. Worker takes a job + moves it to in-flight in one atomic op
# 2. Processes the job
# 3. Removes it from in-flight on success
BRPOPLPUSH queue:jobs queue:worker-42-inflight 0
# 7) Atomic move between lists
LMOVE source destination LEFT RIGHT
# LEFT = from head, RIGHT = to tail (FIFO across the move)
# === Real recipes (Node + ioredis) ===
import Redis from 'ioredis';
const redis = new Redis();
# 8) Recent activity feed (bounded length)
async function pushActivity(userId, activity) {
const key = `feed:user:${userId}`;
await redis.multi()
.lpush(key, JSON.stringify(activity))
.ltrim(key, 0, 99) // keep last 100
.expire(key, 60 * 60 * 24 * 30)
.exec();
}
async function getRecent(userId, n = 20) {
const raw = await redis.lrange(`feed:user:${userId}`, 0, n - 1);
return raw.map(JSON.parse);
}
# 9) Job queue (simple, fast, NOT durable)
async function enqueue(job) {
await redis.rpush('queue:jobs', JSON.stringify(job));
}
async function worker() {
while (true) {
const [, raw] = await redis.blpop('queue:jobs', 30); // 30s timeout
if (!raw) continue;
const job = JSON.parse(raw);
try {
await process(job);
} catch (e) {
await redis.rpush('queue:dead', JSON.stringify({ job, error: e.message }));
}
}
}
# 10) Reliable job queue — atomic move into in-flight
async function reliableWorker(name) {
const flight = `queue:inflight:${name}`;
while (true) {
const raw = await redis.brpoplpush('queue:jobs', flight, 30);
if (!raw) continue;
const job = JSON.parse(raw);
try {
await process(job);
await redis.lrem(flight, 1, raw);
} catch (e) {
await redis.lmove(flight, 'queue:dead', 'RIGHT', 'LEFT');
}
}
}
# Crashed workers' in-flight lists can be reconciled by a janitor:
# move items back to queue:jobs after a timeout.
# 11) Real-time chat ring buffer
async function appendChat(roomId, message) {
const key = `room:${roomId}:chat`;
await redis.rpush(key, JSON.stringify(message));
await redis.ltrim(key, -500, -1); // keep last 500
}
# 12) Stack pattern — LPUSH + LPOP
LPUSH undo-stack action1
LPUSH undo-stack action2
LPOP undo-stack # action2 (LIFO)
# 13) Performance + limits
# - List ops are O(1) at both ends
# - LINDEX, LSET, LREM by value are O(N) — avoid on long lists
# - Max list length: 2^32-1 elements
# - For at-least-once delivery + replay, prefer Streams (XADD/XREADGROUP)
# 14) When to pick lists vs other types
# Lists : queues, recent-N, stacks, activity feeds
# Streams : durable queue with consumer groups, replay, IDs
# Pub/Sub : broadcast, no persistence
# Sorted set : priority queue, leaderboard, time-window
# Hash : object with field-level updates
Why it matters
Lists shine for “cap to last N” feeds and simple job queues. The moment you need at-least-once delivery, retries, or replay, switch to Streams (XADD + consumer groups) — lists drop messages on crash.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
RPUSH queue "job1" "job2" LPUSH queue "job0" LRANGE queue 0 -1 # ["job0","job1","job2"] LPOP queue # "job0" BRPOP queue 5 # blocking pop, 5s timeoutTry it Yourself »
Exercise
Push to the right end of a list.
jobs "job1"
Five letters.
Discussion
Loading…