ML
Redis

Redis Single-Threaded, Explained: The Event Loop Under the Hood

Why Redis commits to a single thread, how the I/O model really works, and where multithreading slips in.

July 22, 20257 min readRedisArchitecture

Redis is famously "single-threaded." That phrase oversells the simplicity and undersells the engineering. The command-execution loop is single-threaded — and that is the reason INCR is atomic without a mutex — but several other things are not.

The core loop

A single thread multiplexes thousands of client sockets with epoll/kqueue. When a command is ready, it is executed in a tight loop, end to end, before the next command is serviced. That's why every single Redis command is atomic: nothing can interleave.

while (!server.shutdown_asap) {
  aeProcessEvents(el, AE_ALL_EVENTS);  // readable fds, writable fds, timers
}

What is not single-threaded

  • Persistence. BGSAVE forks; BGREWRITEAOF forks. The copy-on-write semantics mean memory briefly doubles for hot pages.
  • I/O threads (since 6.0). io-threads 4 offloads socket reads/writes. Command execution is still single-threaded.
  • Lazy freeing. UNLINK and FLUSHDB ASYNC hand memory reclamation to a background thread.

Consequences for your code

A slow command blocks everyone. KEYS * on a million-key database doesn't just take a second — it makes every other client wait a second. Replace it with SCAN:

redis-cli --scan --pattern 'user:*' | head

Same rule applies to SUNIONSTORE over huge sets, DEBUG SLEEP, and very large MGET batches. The slowlog is your friend:

CONFIG SET slowlog-log-slower-than 5000
SLOWLOG GET 10

When to add threads

Enable I/O threads once you are CPU-bound on network serialization (usually above ~100k ops/sec on a single node). Past that, shard — Cluster gives you linear scaling of the command loop itself.

SharePostLinkedIn

Reader Discussion

6 replies// weighed in

TopNewestAuthor
Add to the thread
Disagree, agree harder, or share your own experience…
Email instead →markdown okbe kind
  1. Highlighted by author
    Elena Ricci· Platform Eng · Booking infraFrom experience

    XFetch quietly killed our daily cache stampede. 6h TTL on a product catalog, three-instance API, used to brown-out for 90 seconds every refresh. Shipped XFetch on a Friday afternoon and forgot it existed. That's the highest praise I can give a fix.

    Jul 24, 2025·2 days later
  2. Anya Sokolova· BackendAsks

    any thoughts on Lua vs MULTI/EXEC for the deduct-and-check pattern? been using Lua for 2 years and the script-cache cliff bites us when we redeploy. but MULTI feels chattier

    Jul 28, 2025·6 days later
  3. Sơn Nguyễn🇻🇳 Hà Nội· Senior BackendStory

    +1 cho UNLINK. FLUSHDB SYNC làm prod đứng 39s, alert pager kêu vang nhà — sau đó mình đổi qua UNLINK + SCAN chunked, không bao giờ thấy spike lại. Mọi dev junior team mình bắt buộc đọc cái incident này.

    Jul 27, 2025·5 days later
  4. Luca Bianchi· Tech LeadPushback

    fwiw — hash tags bị overuse là footgun thật. Cluster bọn mình từng có 1 slot ăn 41% traffic vì ai đó nghĩ {tenant} làm key prefix là idea hay. Cluster slowlog từ 12ms lên 800ms trong 1 đêm. Cluster rebalance không cứu được vì cùng 1 slot.

    Jul 28, 2025·6 days later·edited
  5. Huyền Lê· Software EngineerAgrees

    viết postmortem tiêu đề 'WAIT did not wait' xong 1 tuần sau gặp đoạn này trong post. cười ra nước mắt. cái phần WAIT không phải consensus primitive cần tô đỏ trong docs official.

    Jul 25, 2025·3 days later
  6. Isabella Costa· Junior EngineerKind words

    saved this. sharing at standup tomorrow — we've had exactly this problem for 2 sprints and nobody on the team had framed it this way 🙏

    Jul 24, 2025·2 days later

Worked on something similar? Email ducminhldm@gmail.com — I read every one. The good ones become future posts.

Comments seeded · live discussion via email