Redis pipelining and throughput simulator
A Redis server can run hundreds of thousands of simple commands a second, but a client that waits for every reply gets only as many as its network round trips allow. Set the round-trip time, clients and pipeline depth to see where throughput is lost, and add a slow command to watch it hold up everyone.
- One client, by round trips
- 71k/s
- All 50 clients
- 3.56M/s
- The server, by CPU
- 640k/s
- Simulated
- 640k/s
- Median latency
- 1.25 ms
- Worst latency
- 1.25 ms
The server is the limit: it is busy 100% of the time. A deeper pipeline cuts its per-batch I/O cost.
How to use the simulator
Each client sends a batch of commands (one, without pipelining), waits until all the replies are back, then sends the next. The server works on one batch at a time: a fixed cost for reading the request and writing the replies, and a small cost per command. Change the round-trip time, number of clients and pipeline depth, and the panel shows which limit you are hitting: each client’s round trips or the server’s CPU. The default costs, 1 µs per command and 9 µs of socket I/O per batch, are illustrative; they make pipelining’s best case 10 times the unpipelined rate, the figure the Redis documentation gives.
Round trips limit a client
Redis answers simple commands in microseconds, but a client that sends one command and waits for the reply before sending the next can do at most one command per network round trip. The Redis documentation’s example is a slow 250 ms link, which allows at most 4 commands per second however fast the server is; the simulation gives 3.98. Even in one data centre, with a round trip of a fraction of a millisecond, a single waiting client stays far below what the server could do: with a 0.2 ms round trip, one client managed 4,761 commands per second unpipelined and 71,104 with batches of 16.
Pipelining
Pipelining sends many commands before reading any replies. It removes the round trip per command, and it also helps the server: according to the Redis documentation, many pipelined commands are read with a single read() system call and their replies sent with a single write(), and since the switch into the kernel costs far more than executing a simple command, throughput grows almost linearly with pipeline length at first and eventually reaches about 10 times the unpipelined rate. With 50 clients and a 0.2 ms round trip:
| Pipeline depth | Commands per second | 99th percentile latency |
|---|---|---|
| 1 | 99,980 | 500 µs |
| 2 | 181,780 | 550 µs |
| 4 | 307,628 | 650 µs |
| 8 | 470,488 | 850 µs |
| 16 | 639,872 | 1.25 ms |
| 32 | 780,320 | 2.05 ms |
| 64 | 876,480 | 3.65 ms |
| 256 | 965,632 | 13.25 ms |
Throughput keeps rising toward the server’s limit, about 10 times the unpipelined rate with these costs, while each batch takes longer to come back, because a client waits for its whole batch. The server also has to hold every reply of a pipeline in memory until it is sent, which is why the documentation recommends splitting very large jobs into batches of a reasonable size, such as 10,000 commands.
One thread, one command at a time
Redis serves every request from one thread; its documentation calls the design mostly single-threaded, since some slow I/O happens in background threads. Commands run one after another, which makes each one atomic without locks, but as the documentation puts it, when a request is slow to serve, all the other clients wait for it. Commands that touch the whole dataset or a huge value, like KEYS or an LRANGE over a million elements, run for milliseconds or longer while everyone else waits. In the simulation, one 20 ms command every 100 ms took the median latency from 1.25 ms to 1.25 ms and the 99th percentile from 1.25 ms to 21.25 ms. The Redis latency guide recommends finding such commands with the slow log, says KEYS should only be used for debugging, and points to the incremental SCAN family instead.
Pipelines, transactions and scripts
A pipeline is only a network optimisation: the server may run other clients’ commands between the commands of one pipeline. When commands must run together, MULTI/EXEC transactions and Lua scripts execute them as a unit that nothing else interleaves with. Both can be sent pipelined. Multi-key commands such as MGET and MSET are a third way to batch, one command doing the work of many.
Redis pipelining questions and answers
What is Redis pipelining?
Sending several commands without waiting for each reply, then reading all the replies together. The client pays one network round trip per batch instead of per command, and the server reads the batch with one read() call and sends the replies with one write().
How much faster is pipelining?
Over a slow link the round trip dominates: the Redis documentation’s example of a 250 ms RTT allows at most 4 requests per second without pipelining (this page’s simulation gives 3.98). On a fast network the gain comes from fewer system calls; the documentation says throughput grows almost linearly with pipeline length at first and eventually reaches about 10 times the unpipelined rate. With this page’s default costs, 50 clients went from 99,980 to 876,480 commands per second at a depth of 64.
How big should a pipeline be?
Big enough that the round trip and system-call overhead are small next to the work, and no bigger: the server must hold all replies in memory until they are sent. The Redis documentation suggests sending very large jobs in batches of a reasonable size, such as 10,000 commands.
Why does one slow command slow down every client?
Redis executes commands one at a time on a single thread, so a command that takes 20 ms, such as KEYS on a large database, makes every other client wait 20 ms behind it. In this page’s run, adding one such command every 100 ms raised the 99th-percentile latency from 1.25 ms to 21.25 ms.
Is pipelining the same as a transaction?
No. A pipeline only batches the network traffic; other clients’ commands can still run between the commands of a pipeline. MULTI/EXEC transactions and Lua scripts run their commands as one unit that nothing else interleaves with, and can be pipelined too.