On February 2, 2020, Redis 6.0.1 was officially released. In addition to adding new features and new apis, support for multithreading is the biggest change. This article takes a simplified approach to understanding the evolution of the Redis threading model.
Looking for my communication


Redis single-threaded

Strictly speaking, Redis is not single-threaded. There are background threads working, handling slow operations such as the release of useless connections, the deletion of large keys, and so on. Client command requests (socket read), parsing, execution, content return (socket write) are handled by a single thread, so we often say “single thread” refers to the core processing only one thread.

Processing flow

The diagram below:



Redis uses evport, Epoll, KQueue and SELECT to implement multiplexing and increase link processing. The default is Epoll

Refer to aE.c file L47

/* Include the best multiplexing layer supported by this system.
 * The following should be ordered by performances, descending. */
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
    #ifdef HAVE_EPOLL
    #include "ae_epoll.c"
    #else
        #ifdef HAVE_KQUEUE
        #include "ae_kqueue.c"
        #else
        #include "ae_select.c"
        #endif
    #endif
#endifCopy the code

Pros vs. Cons

Advantage:

1. There is no lock problem

2. Avoid CPU switching between threads

Disadvantages:

1. A single thread cannot utilize multiple cpus

2, serial operation, one operation “failure” will “block” subsequent operations


Redis 6 Multithreading

Processing flow



Multithreading is disabled by default. The maximum value of IO_THREADS_MAX_NUM is 128

Network. L2886 c file

/* Initialize the data structures needed for threaded I/O. */
void initThreadedIO(void) {
    io_threads_active = 0; /* We start with threads not active. */

    /* Don't spawn any thread if the user selected a single thread: * we'll handle I/O directly from the main thread. */
    if (server.io_threads_num == 1) return;

    if (server.io_threads_num > IO_THREADS_MAX_NUM) {
        serverLog(LL_WARNING,"Fatal: too many I/O threads configured. "
                             "The maximum number is %d.", IO_THREADS_MAX_NUM);
        exit(1);
    }

    /* Spawn and initialize the I/O threads. */
    for (int i = 0; i < server.io_threads_num; i++) {
        /* Things we do for all the threads including the main thread. */
        io_threads_list[i] = listCreate();
        if (i == 0) continue; /* Thread 0 is the main thread. */

        /* Things we do only for the additional threads. */
        pthread_t tid;
        pthread_mutex_init(&io_threads_mutex[i],NULL);
        io_threads_pending[i] = 0;
        pthread_mutex_lock(&io_threads_mutex[i]); /* Thread will be stopped. */
        if(pthread_create(&tid,NULL,IOThreadMain,(void*)(long)i) ! = 0) { serverLog(LL_WARNING,"Fatal: Can't initialize IO thread.");
            exit(1); } io_threads[i] = tid; }}Copy the code

Join the queue

Network. L3040 c file

/* Return 1 if we want to handle the client read later using threaded I/O.
 * This is called by the readable handler of the event loop.
 * As a side effect of calling this function the client is put in the
 * pending read clients and flagged as such. */
int postponeClientRead(client *c) {
    if(io_threads_active && server.io_threads_do_reads && ! ProcessingEventsWhileBlocked && ! (c->flags & (CLIENT_MASTER|CLIENT_SLAVE|CLIENT_PENDING_READ))) { c->flags |= CLIENT_PENDING_READ; listAddNodeHead(server.clients_pending_read,c);return 1;
    } else {
        return 0;
    }
}

/* When threaded I/O is also enabled for the reading + parsing side, the
 * readable handler will just put normal clients into a queue of clients to
 * process (instead of serving them synchronously). This function runs
 * the queue using the I/O threads, and process them in order to accumulate
 * the reads in the buffers, and also parse the first command available
 * rendering it in the client structures. */
int handleClientsWithPendingReadsUsingThreads(void) {Copy the code

Based on the class diagram



Pros vs. Cons

Advantages:

1. Improve response speed and make full use of CPU

Disadvantages:

1. Increased code complexity

conclusion

  1. Redis multiplexing technology, support epoll, KQueue, selector

  2. In version 5.0 and earlier, there was only one thread to process client requests, serial processing

  3. Version 6.0 introduced worker threads, which only handle network IO reads and writes, while core IO is responsible for serial processing of client instructions


Copyright notice: This article is the author of the [small eyes] original article, article reprint please contact the author

If you read on wechat, please click the link to follow me. If you read on PC, please scan the code to follow me. Welcome to communicate with me and point out mistakes at any time