When we use Redis, we send various commands to Redis and receive the data returned from the commands. But what about the execution of each command? How does Redis handle each command?

Before we dive into the command execution process, we need to take a look at the startup process of Redis.

The start of the Redis

We only look at the single Redis startup process, sentry, cluster, module and so on are skipped first, which makes our analysis easier. Even if it simplifies the analysis process, Redis does a lot of things when it starts up.

There are many omissions in the figure, but it does not affect our subsequent analysis.

As you know, Redis handles network IO through events. As you can see in the figure, Redis creates TCP Handler events through the aeCreateFileEvent function.

void initServer(void) {.../* Create an event handler for accepting new connections in TCP and Unix * domain sockets. */
    for (j = 0; j < server.ipfd_count; j++) {
        if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
            acceptTcpHandler,NULL) == AE_ERR)
            {
                serverPanic(
                    "Unrecoverable error creating server.ipfd file event."); }}if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
        acceptUnixHandler,NULL) == AE_ERR) serverPanic("Unrecoverable error creating server.sofd file event."); . }Copy the code

We only analyze TCP events, ignoring Unix domains. You can see that the TCP event processing handle is acceptTcpHandler.

Connect the Redis

When we connect to Redis with the following command:

$ redis-cli -h host -p port -a password
Copy the code

The TCP connection event is triggered, and the acceptTcpHandler event handle is invoked.

acceptTcpHandler

void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {...// Accept the client request
    cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport); .// Handle client requests
    acceptCommonHandler(cfd,0,cip); . }Copy the code

acceptCommonHandler

static void acceptCommonHandler(int fd, int flags, char *ip) {
    client *c;
    // Create a new client
    if ((c = createClient(fd)) == NULL) {
        serverLog(LL_WARNING,
            "Error registering fd event for the new client: %s (fd=%d)",
            strerror(errno),fd);
        close(fd); /* May be already closed, just ignore errors */
        return; }... }Copy the code

The server keeps the client information for each connection,

struct redisServer {.list *clients;              /* List of active clients */. }Copy the code

createClient

// Create a new client, set the client event, and save the client information to the server.clients queue
client *createClient(int fd) {...if (aeCreateFileEvent(server.el,fd,AE_READABLE,
            readQueryFromClient, c) == AE_ERR)
        {
            close(fd);
            zfree(c);
            return NULL; }... }Copy the code

Here, we create an IO event to read the commands sent by the client. The event handle is readQueryFromClient.

Send the command

redis-cli> set mykey hello
Copy the code

When we send this command to Redis, the readQueryFromClient handle is fired.

readQueryFromClient

ReadQueryFromClient reads the commands sent by the client.

void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
    // Read the command sent by the client and save it in the cache. processInputBufferAndReplicate(c); }Copy the code

processInputBufferAndReplicate

void processInputBufferAndReplicate(client *c) {
    if(! (c->flags & CLIENT_MASTER)) { processInputBuffer(c); }else {
        // If it is the master service, ignore it first. }}Copy the code

processInputBuffer

void processInputBuffer(client *c) {...while(c->qb_pos < sdslen(c->querybuf)) {
        ... // Prejudge, ignore
        // Read the command from the cache and parse the Redis protocol
        / / https://redis.io/topics/protocol Redis protocol description
        // We need to know the function function, but we don't need to know the specific implementation.
        if (c->reqtype == PROTO_REQ_INLINE) {
            if(processInlineBuffer(c) ! = C_OK)break;
        } else if (c->reqtype == PROTO_REQ_MULTIBULK) {
            if(processMultibulkBuffer(c) ! = C_OK)break;
        } else {
            serverPanic("Unknown request type"); }...if(processCommand(c) == C_OK) { ... }... }}Copy the code

processCommand

ProcessCommand executes the command,

/* If this function gets called we already read a whole * command, arguments are in the client argv/argc fields. * processCommand() execute the command or prepare the * server for a bulk read from the client. * * If C_OK is returned the client is still alive and valid and * other operations can be performed by the caller. Otherwise * if C_ERR is returned the client was destroyed (i.e. after QUIT). */
int processCommand(client *c) {.../* Now lookup the command and check ASAP about trivial error conditions * such as wrong arity, bad command name and so forth. */
     // Look up the command in the command dictionary table and set the command handler
    c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr); ./* Exec the command */
    // Execute the command
    // Focus only on the call function, comment out the rest first.
    //if (c->flags & CLIENT_MULTI &&
    // c->cmd->proc ! = execCommand && c->cmd->proc ! = discardCommand &&
    // c->cmd->proc ! = multiCommand && c->cmd->proc ! = watchCommand)
    / / {
    // queueMultiCommand(c);
    // addReply(c,shared.queued);
    //} else {
        call(c,CMD_CALL_FULL); // just focus on call
    // c->woff = server.master_repl_offset;
    // if (listLength(server.ready_keys))
    // handleClientsBlockedOnKeys();
    / /}
    return C_OK;
}
Copy the code

Call executes a specific command implementation function. After that, you just need to focus on the implementation of each command.

Afterword.

The command processing flow call stack looks like this

acceptTcpHandler acceptCommonHandler createClient readQueryFromClient processInputBufferAndReplicate processInputBuffer ProcessInlineBuffer processCommand lookupCommand Call Command implementationCopy the code

Redis command implementation functions, have a fixed naming formatvoid xxxCommand, so you can find it in the source directorysrcLet’s do a direct search. Such assetCommand: