【Redis學習筆記】2018-06-01 set命令執行流程

順風車運營研發團隊 閆昌
一. Redis編譯安裝時指定參數, 防止gdb時被優化, 在make時, 增長參數nooptredis

make  noopt

二. 客戶端通信協議數據庫

1.客戶端與服務端的通信協議是創建在TCP之上的
2.Redis指定了RESP(Redis SerializationProtocol, Redis序列化協議)實現客戶端與服務端的正常交互
3.命令格式: *<參數數量>rn$ <參數1的字節數量>\r\n參數1\r\n$<參數2的字節數量>rn參數2rn
例如: set hello world *3rn$5\r\nset\r\n$5rnworldrn
4.返回結果
狀態回覆: 在RESP中第一個字節爲"+"
錯誤回覆: 在RESP中第一個字節爲"-"
整數回覆: 在RESP中第一個字符爲":"
字符串回覆: 在RESP中第一個字符爲"$"
多條字符串回覆: 在RESP中第一個字節爲"*"app

redis-cli只能看到最終的執行結果, 由於redis-cli自己就是按照RESP進行告終果解析, 因此看不到中間結果
三. set命令執行流程: set hello worldsocket

1.redis-server啓動後, 會進行server.c的main主函數裏
2.當初始化服務端參數以後, 會進入aeMain函數裏
3.aeMain函數裏while循環調用aeProcessEvents, aeProcessEvents裏調用aeApiPoll等待epoll_wait返回可用fd
4.當有客戶端鏈接請求過來以後, 會調用networking.c的createClient函數, 此函數裏調用aeCreateFileEvent, 將第四個參數指定爲: readQueryFromClient
5.當客戶端鏈接成功以後, aeApiPoll返回可用的fd後, 進入aeProcessEvents的for (j = 0; j < numevents; j++)循環, 在此例中, 進入rfileProc函數, 此時的rfileProc爲上一步的readQueryFromClient方法
6.readQueryFromClient函數, ---- networking.c函數

void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
    client *c = (client*) privdata;
    int nread, readlen;
    size_t qblen;
      ...
    readlen = PROTO_IOBUF_LEN;//16K
    ...
    ...
    qblen = sdslen(c->querybuf);//此時的值爲0
    if (c->querybuf_peak < qblen) c->querybuf_peak = qblen;
    c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);//給querybuf分配16K的空間
    nread = read(fd, c->querybuf+qblen, readlen);//從fd中讀取16K的內容, 此時的fd爲客戶端向服務端的socket套接字
    if (nread == -1) {
        if (errno == EAGAIN) {//errno是全局變量, 在read函數後生成的變量, 此變量用一次以後就會消失,  此時EAGAIN表示讀取失敗
            return;
        } else {
            serverLog(LL_VERBOSE, "Reading from client: %s",strerror(errno));
            freeClient(c);
            return;
        }
    } else if (nread == 0) {
        serverLog(LL_VERBOSE, "Client closed connection");
        freeClient(c);
        return;
    } else if (c->flags & CLIENT_MASTER) {
        c->pending_querybuf = sdscatlen(c->pending_querybuf,
                                        c->querybuf+qblen,nread);
    }

    sdsIncrLen(c->querybuf,nread);//擴展querybuf的長度, 使其長度爲讀取出的命令的長度
    c->lastinteraction = server.unixtime;
      ....
    ....
    if (!(c->flags & CLIENT_MASTER)) {
        processInputBuffer(c);//處理命令
    } else {
        size_t prev_offset = c->reploff;
        processInputBuffer(c);
        size_t applied = c->reploff - prev_offset;
        if (applied) {
            replicationFeedSlavesFromMasterStream(server.slaves,
                    c->pending_querybuf, applied);
            sdsrange(c->pending_querybuf,applied,-1);
        }
    }
}
  1. processInputBuffer函數: ---- networking.c
void processInputBuffer(client *c) {
    server.current_client = c;
    while(sdslen(c->querybuf)) {
       ......
       ......
        if (c->argc == 0) {
            resetClient(c);
        } else {
            if (processCommand(c) == C_OK) {//這裏進入執行命令函數時
                if (c->flags & CLIENT_MASTER && !(c->flags & CLIENT_MULTI)) {
                    c->reploff = c->read_reploff - sdslen(c->querybuf);
                }

                if (!(c->flags & CLIENT_BLOCKED) || c->btype != BLOCKED_MODULE)
                    resetClient(c);
            }

            if (server.current_client == NULL) break;
        }
    }
    server.current_client = NULL;
}

8.processCommand函數(server.c)的最後調用了call(server.c)方法oop

9.call方法裏調用了c->cmd->proc方法, 而這個方法就是t_string.c的setCommand方法優化

c->cmd->proc(c);

10.setCommand方法(t_string.c)裏調用了setGenericCommand方法
11.setGenericCommand方法調用了setKey方法
12.setKey方法調用了dbAdd或dbOverwrite方法, 將key和val設置到redis數據庫spa

四. 執行流程unix

clipboard.png

相關文章
相關標籤/搜索