Redis Lists

Lists are linked lists of string values, supporting push/pop operations from both ends in O(1) time. They're ideal for queues, stacks, activity feeds, and any scenario requiring ordered data with fast insertion.

Key Characteristics

• Maximum length: 2^32 - 1 elements (over 4 billion) • O(1) push/pop at head or tail • O(N) access by index (linked list behavior) • Supports blocking operations for pub/sub patterns

Basic Push/Pop Operations

# Push to list
RPUSH queue:jobs "job1" "job2" "job3"   # Add to right (tail)
LPUSH queue:jobs "job0"                 # Add to left (head)

# Pop from list
RPOP queue:jobs                         # Remove from right → "job3"
LPOP queue:jobs                         # Remove from left → "job0"

# Pop multiple
LPOP queue:jobs 2                       # Pop 2 from left
RPOP queue:jobs 3                       # Pop 3 from right

Range Operations

LRANGE queue:jobs 0 -1                  # Get all elements
LRANGE queue:jobs 0 2                   # Get first 3 elements
LRANGE queue:jobs -3 -1                 # Get last 3 elements
LLEN queue:jobs                         # Get list length

Queue Pattern (FIFO)

First In, First Out: add to tail, remove from head.

# Enqueue
RPUSH queue:tasks "task1"
RPUSH queue:tasks "task2"
RPUSH queue:tasks "task3"

# Dequeue
LPOP queue:tasks                        # → "task1"
LPOP queue:tasks                        # → "task2"

Stack Pattern (LIFO)

Last In, First Out: add to head, remove from head.

# Push
LPUSH stack:undo "action1"
LPUSH stack:undo "action2"
LPUSH stack:undo "action3"

# Pop
LPOP stack:undo                         # → "action3"
LPOP stack:undo                         # → "action2"

Blocking Operations

Block until an element is available—perfect for worker queues.

# Block up to 30 seconds for an item
BRPOP queue:jobs 30

# Block on multiple queues (priority order)
BLPOP queue:priority queue:normal 0     # 0 = block forever

# Block and move atomically
BLMOVE source dest LEFT RIGHT 30        # Move with 30s timeout

Index Operations

LINDEX queue:jobs 0                     # Get element at index
LINDEX queue:jobs -1                    # Get last element
LSET queue:jobs 0 "updated_job"         # Update element at index
LPOS queue:jobs "target_value"          # Find position of value

Insert and Remove

# Insert relative to element
LINSERT queue:jobs BEFORE "job2" "job1.5"
LINSERT queue:jobs AFTER "job2" "job2.5"

# Remove occurrences
LREM queue:jobs 1 "job1"                # Remove 1 occurrence
LREM queue:jobs 0 "job1"                # Remove all occurrences
LREM queue:jobs -2 "job1"               # Remove 2 from tail

Trimming

Keep only a range of elements—perfect for capped collections.

# Keep only last 100 notifications
LPUSH notifications:user:1 "new notification"
LTRIM notifications:user:1 0 99

# Activity feed with automatic capping
def add_activity(user_id, activity):
    key = f"feed:user:{user_id}"
    LPUSH(key, activity)
    LTRIM(key, 0, 999)  # Keep only 1000 most recent

Moving Between Lists

# Atomically move element between lists
LMOVE source dest LEFT RIGHT            # Pop from source left, push to dest right
LMOVE source dest RIGHT LEFT            # Pop from source right, push to dest left

# Rotate within same list
LMOVE mylist mylist LEFT RIGHT          # Move head to tail

Use Case: Reliable Queue

Use LMOVE for reliable processing with acknowledgment.

# Producer
RPUSH queue:pending "job_data"

# Worker
while True:
    # Move job to processing list atomically
    job = LMOVE queue:pending queue:processing RIGHT LEFT

    if job:
        try:
            process(job)
            # Remove from processing on success
            LREM queue:processing 1 job
        except Exception:
            # Re-queue on failure
            LMOVE queue:processing queue:pending LEFT RIGHT

Use Case: Recent Activity Feed

def add_activity(user_id, activity_json):
    key = f"activity:{user_id}"

    # Add to feed
    LPUSH(key, activity_json)

    # Keep only last 50 items
    LTRIM(key, 0, 49)

    # Optional: set expiration for inactive users
    EXPIRE(key, 86400 * 30)  # 30 days

def get_recent_activity(user_id, count=20):
    key = f"activity:{user_id}"
    return LRANGE(key, 0, count - 1)
Knowledge is power.