Redis pipelining is how you make Redis do more work with fewer network round trips, dramatically boosting throughput.

Let’s see it in action. Imagine you have a bunch of user IDs and you want to fetch their associated data from Redis. Without pipelining, you’d send each GET command individually, waiting for a response before sending the next.

import redis

r = redis.Redis(decode_responses=True)

user_ids = [f"user:{i}" for i in range(1000)]

# Without pipelining
start_time = time.time()
for user_id in user_ids:
    r.get(user_id)
end_time = time.time()
print(f"Without pipelining: {end_time - start_time:.4f} seconds")

# With pipelining
start_time = time.time()
pipeline = r.pipeline()
for user_id in user_ids:
    pipeline.get(user_id)
results = pipeline.execute()
end_time = time.time()
print(f"With pipelining: {end_time - start_time:.4f} seconds")

The core idea is to send multiple commands to Redis in a single network request and get all the responses back in a single network response. This drastically cuts down the latency associated with network I/O.

Here’s how it works internally:

  1. Client Buffering: When you create a pipeline object (e.g., r.pipeline() in redis-py), the client library starts buffering commands. It doesn’t send them to Redis immediately.
  2. Batching: You add commands to the pipeline object (e.g., pipeline.get("mykey"), pipeline.set("anotherkey", "value")). These commands are queued up in the client’s memory.
  3. Single Request: When you call pipeline.execute(), the client takes all the buffered commands and formats them into a single string, typically separated by \r\n. This entire block of commands is then sent to the Redis server in one go.
  4. Server Processing: Redis receives this single request. It parses the commands, executes them sequentially without sending any intermediate responses back to the client, and then bundles all the results together.
  5. Single Response: Redis sends this bundled response back to the client in a single network packet.
  6. Client Deserialization: The client library receives the response, parses it, and returns a list of results, where each element in the list corresponds to the result of the command at the same index in the pipeline.

The problem pipelining solves is the "latency penalty" of network round trips. For every command sent without pipelining, there’s a send -> wait -> receive cycle. If you have 1000 commands, that’s 1000 round trips, each introducing latency. Pipelining collapses this into one send -> wait -> receive cycle for all 1000 commands.

The key levers you control are:

  • What commands to pipeline: Generally, read commands (GET, HGET, LRANGE, SMEMBERS) are excellent candidates. Write commands (SET, HSET, LPUSH, SADD) also benefit, especially if they are part of a logical group of operations.
  • Pipeline size: While you can pipeline thousands of commands, extremely large pipelines can consume significant client-side memory and potentially overwhelm the Redis server if it needs to process a huge batch at once. A common sweet spot is often in the hundreds or low thousands, depending on your network and server capabilities.
  • EXEC vs. EXEC WITHOUT FLUSH: The standard pipeline.execute() first queues commands and then sends them. pipeline.execute_command('COMMAND') lets you send individual commands in a pipeline, but pipeline.execute() is the primary way to send the whole batch.
  • WATCH and Transactions: Pipelining itself doesn’t provide transactional guarantees. If you need atomicity (all commands succeed or none do), you’ll want to use pipeline.watch(), pipeline.multi(), and pipeline.exec(). This is called a Redis transaction, and it uses pipelining under the hood but adds optimistic locking.

When using redis-py, the pipeline() method returns a Pipeline object. You then call methods on this object corresponding to Redis commands (e.g., pipeline.get('mykey')). Finally, pipeline.execute() sends all queued commands to Redis and returns a list of their results.

The critical detail most developers miss is that pipelining doesn’t make Redis execute commands faster internally. It makes the overall operation faster by reducing network latency. If your Redis server is already CPU-bound or memory-bound, pipelining won’t magically make it more powerful. It’s purely an I/O optimization. You’re still limited by how fast Redis can process commands and how fast your network can carry the data.

The next step after mastering pipelining is understanding Redis transactions and Lua scripting for more complex atomic operations.

Want structured learning?

Take the full Redis course →