Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Learn › Database › Scaling and distribution

Redis single-threaded execution

If one command is slow, everyone waits

Redis runs commands one at a time, in order, on a single thread. That is why each command runs without anything cutting in, even without locks, but if one command takes a long time, every command behind it has to wait in line.

Partly checked against real execution · The principle was measured and checked on Redis 8.10.2 (the time values are examples)

Concepts in this lab

Redis
A key-value store that keeps data in memory. It provides not only strings but also data structures such as lists, hashes, sets, and sorted sets. It is very fast because it works in memory, and it is used for caches, session stores, distributed locks, leaderboards, rate limiting, and more. It runs commands one after another on a single thread.
Cache
Storing frequently used data or computed results ahead of time in a place that can be accessed quickly, and reusing them. Responses get faster and the load on the source drops, because the source (usually a database) is not queried every time. In return, you must manage the problem of stale values that remain when the source changes.
Atomicity and CAS (compare-and-swap)
Atomicity is the property that an operation made of several steps runs as one, with no gap for anything to cut in. CAS (compare-and-swap) performs "change to the new value only if the current value equals the value I read" as a single atomic operation, which makes safe updates possible without locks. AtomicInteger and similar classes use it.
Event loop
This is a structure in which a small number of threads detect the I/O events that occur on many connections and process them in turn. There is no thread per connection, so one thread takes care of many connections. If you run a long task or a blocking call on such a thread, processing stops for every connection that the thread is responsible for.

Try these

Running with the default settings

  1. Redis receives the commands of several clients, puts them in a single line, and one thread runs them one by one from the front. The next command starts only after the current one has finished.
  2. Service A GET product:1: It ran for 0.1ms.
  3. Service B GET product:2: It ran for 0.1ms.
  4. Admin KEYS user:*: It ran for 500ms. While this command runs, Redis cannot process any other command.
  5. Service A GET product:3: After waiting 499.2ms for the preceding command to finish, it ran for 0.1ms.
  6. Service B SET cart:9 ...: After waiting 499.3ms for the preceding command to finish, it ran for 0.1ms.
  7. Service A GET product:4: After waiting 498.4ms for the preceding command to finish, it ran for 0.1ms.

Result of this run

Code

// Services A and B: ordinary cache lookups
String v = redis.opsForValue().get("product:1");   // 0.1ms

// Admin script
Set<String> keys = redis.keys("user:*");          // scans all the keys at once

Assumptions of this model

The run times of the commands (GET 0.1ms, KEYS 500ms, reading a large list 300ms, one SCAN 0.5ms) are examples to show the difference in size. That Redis runs commands one after another in a single thread, and the recommendation to use SCAN instead of KEYS, follow the official Redis documentation. Since Redis 6, several threads can be used for network I/O, but running commands is still done by one thread. The principle was checked on a real Redis 8.10.2 (1 million keys, a list of 500,000 elements). While KEYS was running, a GET from another client waited up to 186ms, and during the large list query it waited 22ms (usually 3ms or less), and while SCAN walked through in pieces of 1,000, it never exceeded 1.5ms. The final value of INCR sent by two clients at the same time, 20,000 times each, was exactly 40,000. The time values on the screen are examples, not these measured values, and the real times vary with the data size and the hardware.

Previous lab: Partitioning · Next lab: NoSQL and the document model

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these