Redis List Data Structure and Common Commands
Redis Lists are a lightweight, ordered collection of strings that preserve the insertion order. They are implemented as a double‑linked list or a compressed list, depending on size, and provide O(1) operations at both ends. These properties make Lists ideal for building message queues, task schedulers, sliding windows, and any scenario where order matters and frequent push/pop operations occur.
Core Characteristics
- Ordered Sequence
Elements are stored in the exact order they arrive. The leftmost element is index0, while the rightmost element is index-1. - Bidirectional Operations
LPUSH/RPUSHadd elements to the left or right;LPOP/RPOPremove from the left or right. All run in constant time. - Adaptive Storage
Redis chooses between a ziplist (compact array) for small lists and a linkedlist for larger ones, balancing memory usage and speed. - Index‑Based Access
Commands likeLINDEX,LSET, andLINSERTlet you read or modify elements at a specific position, but these run in linear time (O(N)), so they’re best used sparingly.
Command Overview
| Category | Command | Typical Use |
|---|---|---|
| Insertion | LPUSH, RPUSH, LPUSHX, RPUSHX |
Add one or more items to the left or right; the X variants only act if the key already exists. |
| Removal | LPOP, RPOP, LREM, LTRIM |
Pop the first or last element, delete specific values, or keep only a sub‑range. |
| Inspection | LLEN, LRANGE, LINDEX |
Get length, slice a range, or fetch a single element. |
| Modification | LSET, LINSERT |
Replace an element or insert before/after a match. |
| Blocking | BLPOP, BRPOP, BRPOPLPUSH |
Wait for an element to appear, useful for consumer processes. |
| Counting | LCOUNT (Redis 7+) |
Count elements that match a pattern. |
Detailed Command Walkthrough
1. Adding Elements
# Push three items onto the left side; returns new length
LPUSH mylist "a" "b" "c" # → 3
# Append a single item to the right
RPUSH mylist "d" # → 4
# Only push if the list already exists
LPUSHX mylist "e" # → 5
2. Removing and Popping
# Remove the leftmost element
LPOP mylist # → "c"
# Remove the rightmost element
RPOP mylist # → "d"
# Delete all occurrences of "b"
LREM mylist 0 "b" # → 1 (number of removed items)
3. Slicing the List
# Retrieve elements from index 0 to 2 inclusive
LRANGE mylist 0 2 # → ["a","e","c"]
4. Index‑Based Operations
# Read the element at index 1
LINDEX mylist 1 # → "e"
# Replace the element at index 2
LSET mylist 2 "x" # → OK
# Insert "y" after the first occurrence of "a"
LINSERT mylist AFTER "a" "y" # → 5
5. Blocking Pop (Consumer Pattern)
# Block on the left side for up to 5 seconds
BLPOP mylist 5 # → ["mylist","a"]
6. Trimming the List
# Keep only elements with indices 0 through 3
LTRIM mylist 0 3 # → OK
Building a Simple Task Queue
Below is a minimal Python example that demonstrates how to use a List as a FIFO queue. The producer pushes JSON‑encoded tasks to the right; the consumer blocks on the left, processes the task, and repeats.
import redis, json, time
r = redis.StrictRedis(host='127.0.0.1', port=6379, db=0)
def enqueue(task):
"""Push a task onto the queue (right side)."""
r.rpush('task_queue', json.dumps(task))
def worker():
"""Consume tasks with a blocking pop."""
while True:
key, data = r.blpop('task_queue', timeout=10)
if data:
task = json.loads(data)
print(f"Processing task {task['id']}")
time.sleep(task['duration'])
else:
print("No tasks, waiting...")
# Example usage
enqueue({'id': 1, 'duration': 2})
enqueue({'id': 2, 'duration': 1})
enqueue({'id': 3, 'duration': 3})
# worker() # Uncomment to start the consumer loop
Why it works well
RPUSHguarantees that new tasks are added to the tail, preserving order.BLPOPblocks until an item is available, eliminating busy‑waiting and reducing CPU usage.- The queue is atomic from the perspective of each command, so concurrent producers and consumers won’t corrupt the order.
Best Practices & Gotchas
Keep the List Short When Possible
Operations likeLRANGEandLINDEXare linear in the list length. UseLTRIMto discard old entries, especially for sliding‑window logs or recent‑activity feeds.Avoid Large Payloads
Elements should stay within a few kilobytes. Large strings force Redis to switch from a ziplist to a linkedlist, increasing memory fragmentation.Set Reasonable Blocking Timeouts
In a producer/consumer model, choose a timeout that balances responsiveness with connection overhead. Too long a timeout ties up connections; too short leads to frequent reconnections.Use Transactions or Lua for Atomicity
When you need to perform several list operations together (e.g., trim after popping), wrap them inMULTI/EXECor anEVALscript to avoid race conditions.Monitor Size & Memory
INFO keyspaceandMEMORY USAGE keyhelp you track how many lists you have and how much memory each consumes. Plan capacity accordingly.Prefer Lists for FIFO
If you need a priority queue, consider using a sorted set (ZADD,ZRANGE,ZPOPMIN) instead of a list.
Summary
Redis Lists offer a simple yet powerful way to manage ordered collections with fast, constant‑time operations at both ends. By mastering the core commands—LPUSH, RPUSH, LPOP, RPOP, LRANGE, and the blocking variants—you can build efficient message queues, task schedulers, and real‑time feeds. Coupled with trimming, transactions, and careful monitoring, Lists remain a cornerstone of high‑performance Redis applications.