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 directorysrc
Let’s do a direct search. Such asset
Command: