Foundations 3 of 5 · Visual guide

Atomicity and the single thread

Redis executes one command at a time. That one fact makes INCR safe, makes KEYS * dangerous, and explains MULTI and Lua.

One queue, one executor

The Redis event loop Three clients send commands at the same time. They enter one queue in arrival order. A single thread executes each command completely before starting the next, so INCR is never interrupted. A slow command blocks everything behind it. client A client B client C waiting… waiting… waiting… command queue A: INCR views B: INCR views C: GET views X: KEYS * (2 s) 1 thread executes views 10
  1. Three clients send commands at the same instant: A and B both INCR views, C reads it.
  2. They land in one queue in arrival order. Network reads and writes can use I/O threads (Redis 6+), but commands still run on one thread.
  3. The thread runs A's INCR to the end: read 10, add 1, write 11. Nothing else can run in between.
  4. Then B's INCR: 11 → 12. Then C's GET sees 12. No locks were needed anywhere.
  5. The flip side: one slow command — KEYS *, HGETALL on a million-field hash, a long Lua script — makes every client behind it wait.
Every single command is atomic. Two commands in a row are not — something can run between them.

Race: GET+SET vs INCR

Lab · two app servers each add 100 likes at the same time

expected200
actual—

    Run both. The trace shows the first operations in the order Redis received them.

    Group commands: MULTI / EXEC

    A MULTI/EXEC transaction The client sends MULTI, then two commands that are queued, then EXEC. Redis runs both queued commands back to back with nothing in between. client Redis MULTI OK DECRBY stock 1 QUEUED RPUSH orders o9 QUEUED EXEC DECRBY → 2 · RPUSH → 1 back to back, nothing in between a runtime error in one ≠ rollback of the other EVAL "if GET stock > 0 then DECR; RPUSH … end" 0 read → decide → write, all inside one atomic script
    1. MULTI opens a transaction. Redis replies OK and starts queueing.
    2. DECRBY and RPUSH reply QUEUED. Nothing has run yet, so you cannot read their results inside the transaction.
    3. EXEC runs the whole queue in one go. No other client's command can slip in between.
    4. There is no rollback. If one command fails at run time (wrong type), the others still apply. Validate first.
    5. Need to read, decide, then write (“only if stock > 0”)? Use a Lua script — it runs as one atomic command — or WATCH + MULTI and retry.
    MULTI/EXEC = “run these together”. Lua = “run this logic together”.

    Pipeline, MULTI, WATCH, Lua

    Four ways to send more than one command
    PipelineSend 100 commands without waiting for each reply. Faster, not atomic — others can interleave.
    MULTI / EXECQueue, then run as a block. Atomic, no logic — you cannot branch on a value.
    WATCH + MULTIRead, decide in the app, EXEC fails if a watched key changed. Optimistic — retry on failure.
    Lua (EVAL / FUNCTION)Logic runs inside Redis. Atomic with logic — keep scripts short; they block like any command.

    Try a transaction

    Quick check

    Your app does GET balance, adds 50, then SET balance. Two requests do it at once. What can happen?
    Show answer

    A lost update: both read the same old value. Use INCRBY balance 50, or a Lua script if there is a rule like “never below 0”.

    Is a pipeline of SET a and SET b atomic?
    Show answer

    No. It only saves round trips. Another client can run between them. Wrap them in MULTI/EXEC if they must land together.