25 How Does Redis Handle Time Consuming System Calls

25 How Does Redis Handle Time-consuming System Calls #

In this lesson, we will mainly learn about solving the problem of potentially timeout system calls through BIO threads, as well as the tasks and processing flow handled by BIO threads.

BIO Thread Introduction #

During the operation of Redis, some slow-running and blocking tasks are inevitably generated, such as synchronizing file buffers in the kernel to disk and closing files, which will cause short-term blocking. There are also some large keys, such as aggregation-type elements with a high number of elements, reaching tens of thousands or more. When deleting these elements, the entire process takes a long time because all elements need to be released and recycled one by one. The core processing thread of Redis adopts a single-process single-thread model, and all commands’ reception and processing, data eviction, etc. are performed in the main thread, which is very fast. If the core single thread still needs to handle those slow tasks, it will inevitably block normal requests from users during processing, leading to service latency. Therefore, Redis introduces the Background I/O (BIO) thread to specifically handle those slow tasks, thereby ensuring and improving the processing capacity of the main thread.

img

Redis’ BIO thread adopts a producer-consumer model. The main thread is the producer, producing various slow tasks and then storing them in the task queue. The BIO thread is the consumer, getting tasks from the queue and processing them. If the producer produces tasks too quickly, the queue can be used to buffer these tasks, avoiding overload or data loss. If the consumer processes tasks quickly, it can quietly wait after processing is completed without adding additional performance overhead. When there are new tasks, the main thread uses condition variables to notify the BIO thread, so that the BIO thread can perform tasks again.

BIO Task Handling #

When Redis starts, it creates three task queues and builds three BIO threads, with each BIO thread corresponding to one task queue. The three BIO threads handle the following three types of tasks.

  1. Close file task: After rewriteaof is completed, the main thread needs to close the old AOF file. It inserts a close task for the old AOF file into the close queue, which is then handled by the close thread.
  2. Fsync task: After Redis writes AOF data to the file system cache, it needs to periodically write the system cache data to the disk. At this moment, it can write a task of synchronizing file cache to the fsync queue, which is then handled by the fsync thread.
  3. Lazyfree task: When Redis needs to evict elements from aggregated data types such as lists, sets, hashes, etc., with the number of elements exceeding 64, it writes the objects to be recycled into the delay cleanup queue. The lazyfree thread will asynchronously recycle them later.

Processing Flow of BIO #

The entire processing flow of the BIO thread is shown in the figure below. When the main thread has a slow task that needs to be processed asynchronously, it will submit the task to the corresponding task queue. When submitting a task, first, memory space is allocated and a BIO task is constructed. Then, the lock on the queue is locked, a new BIO task is added to the end of the queue, and finally, an attempt is made to wake up the BIO thread that is waiting for a task.

img

When the BIO thread starts or finishes processing all tasks and finds that the task queue is empty, it will block and wait for new tasks to arrive. When the main thread has a new task, it will submit the task and wake up the BIO thread. The BIO thread will then start polling for new tasks and process them. After processing all BIO tasks, it will enter the blocking state again, waiting for the next round of awakening.