Skip to content

Redis - Lists

Redis Lists are ordered collections of strings, sorted by insertion order. They are implemented as Linked Lists, which makes it very fast to add new elements to either the head (left) or the tail (right) of the list, even for lists with millions of elements.

Key operations like LPUSH (add to head) and RPOP (remove from tail) are O(1), meaning their speed is constant regardless of the list’s size. The maximum length of a list is 2^32 - 1 elements (over 4 billion).

Lists are ideal for implementing producer-consumer patterns like a task queue. A ‘producer’ application pushes tasks onto a list, and one or more ‘worker’ applications pull tasks off the list for processing.

The producer uses LPUSH to add new tasks to the head of a list named task_queue. We’ll use JSON strings to represent structured tasks.

# Producer adds three tasks to the queue
redis> LPUSH task_queue '{"type":"send_email", "recipient":"user1@example.com"}'
(integer) 1
redis> LPUSH task_queue '{"type":"generate_report", "report_id":"xyz-123"}'
(integer) 2
redis> LPUSH task_queue '{"type":"process_image", "image_id":987}'
(integer) 3

A worker can use RPOP to get the oldest task from the tail of the list. However, a much better approach is to use the blocking command BRPOP. It atomically removes and returns the last element, but if the list is empty, it will block the connection until a new element is available or a timeout is reached. This is far more efficient than constantly polling the list.

# Worker asks for a task, blocking for up to 10 seconds if none are available.
# It receives the first task that was pushed (FIFO - First-In, First-Out).
redis> BRPOP task_queue 10
1) "task_queue"
2) "{\"type\":\"send_email\", \"recipient\":\"user1@example.com\"}"
# Worker asks again and gets the next task
redis> BRPOP task_queue 10
1) "task_queue"
2) "{\"type\":\"generate_report\", \"report_id\":\"xyz-123\"}"
# If the worker asks again and the queue is empty, the command will wait.
# After 10 seconds, if no producer has pushed a new task, it will return (nil).
redis> BRPOP task_queue 10
(nil)
(10.00s)
  • Capped Lists: For use cases like storing recent logs or activities, use LPUSH to add items and LTRIM to periodically trim the list to a fixed size. This prevents unbounded memory growth.
  • Avoid Mid-List Operations: Commands like LINDEX (get element at index) and LINSERT (insert element at index) are slow (O(N)). They require traversing the list from the head or tail. For high-performance applications, stick to head/tail operations (LPUSH, RPUSH, LPOP, RPOP).
  • For Reliable Queues, Consider Streams: While lists are great for simple queues, they lack features like message acknowledgment and consumer groups. If you cannot afford to lose a single task (e.g., if a worker crashes after RPOP but before finishing), you should use the more robust Redis Streams data structure.
CommandDescription
LPUSH key element [element …]Prepends one or multiple elements to the head (left) of a list.
RPUSH key element [element …]Appends one or multiple elements to the tail (right) of a list.
LPOP key [count]Removes and returns the first element (from the head) of a list.
RPOP key [count]Removes and returns the last element (from the tail) of a list.
BLPOP key [key …] timeoutBlocking version of LPOP. Blocks until an element is available or timeout occurs.
BRPOP key [key …] timeoutBlocking version of RPOP. The preferred command for reliable worker queues.
LLEN keyGets the length of a list.
LRANGE key start stopGets a range of elements from a list. Use with caution on very long lists.
LTRIM key start stopTrims a list to the specified range, removing elements outside of it.