avatarGejiufelix

Summary

Redis operates primarily as a single-threaded application but has introduced multi-threading features in version 6 to address performance bottlenecks related to network IO operations and non-connected events.

Abstract

Redis is fundamentally built upon a single-threaded model utilizing the Reactor pattern, which allows it to handle multiple connections efficiently with minimal thread switching and resource wastage. This design leverages I/O multiplexing to manage numerous client requests, ensuring high performance for memory-based operations with low command operation time complexity. Despite its single-threaded nature, Redis has incorporated multi-threading in its version 6 to mitigate the limitations of network IO operations, which can cause significant CPU time consumption and block subsequent operations when dealing with large key-value pairs. The multi-threading enhancements are specifically aimed at improving the handling of network data reading and writing, as well as protocol parsing, while maintaining single-threaded command execution for consistency and simplicity.

Opinions

  • The author emphasizes that early Redis implementations are unequivocally single-threaded, clarifying any confusion about its threading model.
  • The traditional blocking IO model is deemed inefficient due to its tendency to create a new thread for each client request, leading to frequent CPU context switching and potential resource exhaustion.
  • Pseudo-asynchronous IO models, while an improvement over the traditional model, still suffer from potential thread pool blockages, limiting the maximum number of connections that can be handled.
  • The Reactor design pattern is presented as a superior alternative, allowing Redis to handle multiple I/O operations within a single thread, thus avoiding the drawbacks of thread-per-connection models.
  • The introduction of multi-threading in Redis 6 is seen as a strategic enhancement to address specific performance bottlenecks without deviating from the core single-threaded operation model that ensures atomicity and simplicity.
  • The article suggests that while Redis's single-threaded approach is highly performant for most operations, certain scenarios, such as deleting large key-value pairs, can benefit significantly from multi-threaded processing.

Go crazy, is Redis single-threaded or multi-threaded?

Photo by Amanda Kerr on Unsplash

For early programmers, Redis is single-threaded, no doubt about it. So why do many friends have doubts about this now? Has Redis changed? Today we understand it all at once. Let’s take a look at these summaries:

After looking at the picture, let’s take a look at the content of each point in detail:

1. First, let’s take a look at the Reactor pattern

(1) Before explaining the Reactor mode, let’s first understand the traditional blocking IO model Before explaining the text, we will integrate it with examples in life, so it will be easier to understand. For example, if you go to the store to buy the clothes you want, after reading the price, you find that it is too expensive, and then you leave, then the shopping guide waiting for you to place an order during this time is equivalent to a corresponding thread, and you want to place an order. It can be regarded as a connection request alone.

After you have walked around from the outside, you find that the items in this store are the most cost-effective, and then you re-select the purchased products, then the shopping guide will follow you to complete the purchase of this product. During this time period, the waiter is waiting and does nothing else, which is equivalent to blocking.

Summary: In the traditional blocking IO model, a separate Acceptor thread monitors the client’s connection. Whenever a client requests, it will assign a new thread to the client for processing. When multiple requests come at the same time, the server will allocate a corresponding number of threads accordingly. This will cause the CPU to switch frequently and waste resources.

(2) Let’s understand the pseudo-asynchronous IO model together For a better understanding, let’s take shopping in a mall as an example:

After the owner of the mall has been operating for a period of time, customers gradually hide. Assuming that the original 10 waiters in the mall provide one-on-one service, then this model cannot cope. So the boss uses a 10-person thread pool. That is, after serving one customer, the waiter immediately goes to serve another.

Of course, many people will start to think about it. This will cause a problem. When some customers are very slow to purchase, the waiter will wait for a long time until the customer’s purchase is over. Assuming that 10 customers are very slow to buy, then these 10 waiters have to wait forever, which will lead to the state that the rest of the customers are not served. This is the case where all the threads of the thread pool we mentioned above are blocked. Summarize: When a client accesses, encapsulate the client’s request into a task and deliver it to the backend thread pool for processing. The thread pool maintains a message queue and multiple active threads to process tasks in the message queue; this solution avoids the problem of thread resource exhaustion caused by creating a thread for each request. But the bottom layer is still a synchronous blocking model. If all threads in the thread pool are blocked, it will not be able to respond to more requests. Therefore, this mode will limit the maximum number of connections, there are certain optimizations, but it cannot be solved fundamentally.

Photo by Dieter de Vroomen on Unsplash

(3) After everyone has watched it for so long, it’s time for the Reactor design pattern to appear. Let’s take shopping in a shopping mall as an example: the owner of the shopping mall also discovered the problem that customers are slow in placing orders, so he thought of another way and left a waiter. When the customer places an order, the waiter will go to entertain other customers, and the customer can directly call the waiter to serve after purchasing. customers and servers can be seen as multiple connections and one thread, respectively. The waiter is stuck at one customer, and when another customer buys, she immediately goes to serve other customers.

Summary: Unlike traditional IO multi-thread blocking, in the I/O multiplexing model, multiple connections share a blocking object, and the application only needs to wait on one blocking object. When a connection has new data to process, the operating system notifies the application, and the thread returns from the blocked state to start business processing. Reactor monitors client request events through the I/O multiplexing program, and distributes them through the task dispatcher after receiving the event.

Photo by dan carlson on Unsplash

2. After the above description, we should know why Redis is single-threaded?

Redis is implemented based on the Reactor single-threaded mode, based on memory, and the command operation time complexity is low, so the read and write rate is very fast After the IO multiplexer receives the user’s request, it pushes it all into a queue and hands it to the file dispatcher. For subsequent operations, as seen in the reactor single-threaded implementation, the entire process is completed in one thread, so Redis is called a single-threaded operation.

3. So why is Redis related to multithreading again?

More senior friends should know that multi-threading was introduced in the Redis6 version. In the above description, we said that Redis single-threaded processing has a very fast speed, so why introduce multi-threading? (1) First understand the performance bottleneck of single thread in Redis Mainly in network IO operations, that is to say, most of the CPU time is occupied during the execution of read/write system calls to read and write network. Then in the project, if you need to delete some large key-value pairs, it cannot be deleted in a short time, then for a single thread, the subsequent operations will be blocked. (2) Bottleneck for non-connected events Reactor will call the corresponding handler to complete the process from read to business processing and then to write processing, which means that this step will cause performance bottlenecks. Summarize: Redis is designed to handle network data reading and writing and protocol parsing through multi-threading. For command execution, single-threaded operations are still used

Finally

Thank you for reading, look forward to your attention.

Redis
Concurrency
Multitasking
Programming Tips
Programming
Recommended from ReadMedium