Redis path · Why it is fast · Topic 1 of 11

One command at a time, and why that is fast

Not started

Redis keeps its whole dataset in memory and serves every client from a single event loop. Requests arrive on many connections; the loop reads whichever sockets are ready, executes one command at a time, and writes the replies. Because nothing else touches the data while a command runs, there are no locks, no lock contention and no partial updates: every single command is atomic. Most commands finish in microseconds, so one thread is enough for a very large number of operations per second.

Why it matters: Redis's speed and its biggest operational trap (one slow command stalls everyone) have the same cause.

  1. 1Data lives in memory

    Every read and write touches RAM, not disk, so a typical command takes microseconds.

  2. 2One thread runs commands

    An event loop serves all clients, executing one command at a time, so each command is atomic and there are no locks.

  3. 3Slow commands block everyone

    A command that walks a million keys (KEYS, a huge SMEMBERS, a long Lua script) holds up every other client until it ends.

  4. 4The network is usually the limit

    Round trips cost far more than command execution; pipelining sends many commands without waiting for each reply.

1

How it works

The explanation, with examples you can run.

Mostly single-threaded

"Single-threaded" describes command execution, not the whole process. Redis uses background threads for things like closing files and freeing large values (UNLINK frees memory off the main thread, unlike DEL), and forks a child process for snapshots. Since Redis 6 it can also use I/O threads (io-threads, off by default) to read and write sockets in parallel; the commands themselves still run one at a time. To use many cores for commands, you run several Redis processes, or a cluster.

The trap: one slow command

The flip side of one command at a time is that a slow command stops everyone. Commands are not all O(1):

KEYS user:*            # walks every key in the database; debugging onlySMEMBERS big:set       # returns every member; fine for 50, not for 5 millionDEL huge:hash          # frees every field on the main thread; UNLINK does it in the backgroundLRANGE log 0 -1        # the whole list

While one of these runs, every other client's command waits. The usual fixes: SCAN (and HSCAN, SSCAN, ZSCAN) iterate with a cursor in small batches; UNLINK instead of DEL for large keys; keep collections bounded; keep Lua scripts short. The slow log records commands over a threshold:

CONFIG SET slowlog-log-slower-than 10000     # microsecondsSLOWLOG GET 10

Snapshotting has a similar effect: fork() of a process with a large memory footprint can pause the main thread for milliseconds or more.

Round trips cost more than commands

A command that takes a microsecond on the server may take half a millisecond or more to travel over the network and back. A loop of 1,000 GETs, each waiting for its reply, spends almost all its time waiting. Two fixes:

  • Multi-key commands: MGET k1 k2 k3, HMGET, MSET.
  • Pipelining: the client writes many commands without waiting, then reads all the replies. Redis executes them in order; other clients' commands may run in between, so a pipeline is not a transaction.
# Send three commands in one go from the shell; --pipe reports a summary, not each replyprintf 'SET a 1\r\nINCR a\r\nGET a\r\n' | redis-cli --pipe# All data transferred. Waiting for the last reply...# Last reply received from server.# errors: 0, replies: 3

In application code, client libraries expose pipelines directly (a pipeline() or batch object that queues commands and sends them together).

What this means for design

Keep values and collections at sizes where each command is cheap. Prefer commands that do the work on the server (INCR, ZADD, HINCRBY) over reading, changing and writing back from the client. And treat Redis as a shared resource: one service running an expensive command can slow down every other service using the same instance.

2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.An engineer runs KEYS user:* on a production instance with 50 million keys. What happens?
  2. 2.Two clients run INCR counter at the same moment. Can an increment be lost?
  3. 3.A service does 1,000 GETs per request, each waiting for its reply. What is the cheapest big improvement?

0 of 3 right so far. Get all three to finish this topic.