深入Redis命令的執行過程

深入Redis命令的執行過程

Redis 服務器: Redis 服務器實現與多個客戶端的連接,並處理這些客戶端發送過來的請求,同時保存客戶端執行命令所產生的數據到數據庫中。Redis 服務器依靠資源管理器來維持自身的運轉,其主要作用是管理 Redis 服務。

服務器處理命令的過程

我們向客戶端發送了一條命令:SET city〝beijing〝

第一步 用戶將命令 SET city〝beijing〝輸入客戶端,客戶端接收到此命令。
第二步 客戶端會先將接收到的命令轉化為服務器可以識別的協議格式,然後利用連接到服務器的套接字,將轉化為合法協議格式的命令請求發送給服務器。
深入Redis命令的執行過程

第三步 當服務器接收到客戶端傳遞過來的協議數據時,客戶端與服務器之間的連接套接字就會變得可讀,此時,服務器將會調用命令請求處理器執行以下過程。

​ 3.1 服務器讀取套接字中協議格式的命令請求,然後將讀取到的命令請求保存到客戶端狀態的輸入緩衝區中。

​ 3.2 對輸入緩衝區中的命令請求進行分析,獲取命令請求參數及參數個數,分別保存到客戶端狀態的 argv 和 argc 屬性中。

​ 3.3 調用命令執行器,執行客戶端發送過來的命令請求。


​ 命令執行器在執行客戶端發送過來的命令請求的過程中,會先根據客戶端狀態的 argv[0]參數,在命令表(Command Table)中查找參數所指定的命令,並將查找到的命令保存到客戶端狀態的 cmd 屬性中,然後進行相關的判斷,比如,判斷客戶端狀態的 cmd 指針是否指向 NULL,或者檢查客戶端的身份,判斷是否驗證通過等,最後調用命令的實現函數執行相關命令,這就是命令執行器的執行過程。


​ 命令表是一個字典,用於存放 Redis 的命令,字典的鍵就是一個個命令的名字,比如「set」「sadd」「zadd」等;而字典的值是一個個 redisCommand 結構,而每個 redisCommand 結構記錄了對應命令的實現信息。redisCommand 結構具有多個屬性,具體如下。

● name 屬性:表示命令的名字,比如 SET、GET,它是 char*類型的。

● proc 屬性:它是一個函數指針,用於指向命令的實現函數,比如,指向 SET 命令的實現函數 setCommand,它是 redisCommandProc類型的,而 redisCommandProc 類型的定義為 typedef void redisCommandProc(redisClientc)。

● arity 屬性:它是一個 int 類型的整數,表示命令參數的個數,用於判斷命令請求的格式是否正確。如果 arity 屬性的值是一個負數-N,則表示命令參數的數量大於等於N。請注意,這裡所說的參數個數包含命令的名字本身,比如,SET city〝beijing〝,這條命令的參數個數是 3,分別是「SET」「city」「beijing」。

● sflags 屬性:它是一個 char*類型的字符串形式的標識值,具有多個標識符,用於記錄這個命令所具有的屬性。

● flags 屬性:它是一個 int 類型的整數,是對 sflags 標識進行分析得出的二進制標識,這個二進制標識由程序自動生成。當服務器對命令標識進行檢查時,使用的是 flags 屬性。

● calls 屬性:該屬性用於統計服務器共執行了多少次這個命令,它是一個 long long 類型的整數。

● milliseconds 屬性:該屬性用於統計服務器執行這個命令所耗費的總時長,它是一個 long long 類型的整數。

每個 Redis 命令都有其對應的 redisCommand 結構,都有上面的相關屬性。

sflags 屬性所具有的標識符具體如下。

● a:屬性值為 a,表示這個命令是一個 Redis 管理命令。相關的命令有 SAVE、BGSAVE、SHUTDOWN 等。

● l:屬性值為 l,表示這個命令常用於服務器載入數據的過程中。相關的命令有 INFO、PUBLISH、SUBSCRIBE 等。

● m:屬性值為 m,說明這個命令在執行的過程中可能會佔用大量內存。在執行之前,需要判斷服務器的內存大小及使用情況,如果服務器的內存資源不足,則將會拒絕執行這個命令。相關的命令有 SET、SADD、APPEND、RPUSH、LPUSH 等。

● M:屬性值為 M,表示這個命令在 Redis 監視器模式下不會被自動傳播。相關的命令有 EXEC。

● p:屬性值為 p,說明這個命令與 Redis 的消息訂閱發佈功能相關。相關的命令有 PUBLISH、PUBSUB、PSUBSCRIBE、PUNSUBSCRIBE、SUBSCRIBE、UNSUBSCRIBE 等。

● r:屬性值為 r,只讀,說明這是一個只讀命令,用於獲取相關數據,它不會修改數據庫。相關的命令有 GET、STRLEN、EXISTS 等。

● R:屬性值為 R,說明這是一個隨機命令,在處理相同的數據集和相同的參數時,得到的結果是隨機的。相關的命令有 SPOP、SSCAN、RANDOMKEY 等。

● s:屬性值為 s,表示在 Lua 腳本中不能使用該命令。相關的命令有 BLPOP、BRPOP、SPOP、BRPOPLPUSH 等。

● S:屬性值為 S,表示這個命令在 Lua 腳本中可以使用。在 Lua 腳本中使用這個命令時,輸出的結果會被排序,也就是輸出的結果是有序的。相關的命令有 KEYS、SUNION、SDIFF、SINTER、SMEMBERS 等。

● t:屬性值為 t,表示這個命令允許從服務器在帶有過期數據時使用。相關的命令有 PING、INFO、SLAVEOF 等。

● w:屬性值為 w,可寫,說明這是一個寫入命令,它可以修改數據庫。相關的命令有 SET、DEL、RPUSH 等。


第四步 命令執行器在執行完相關的實現函數之後,服務器會接著做一些後續工作,然後將命令結果返回給客戶端。

​ (1)在命令執行的過程中會耗費一些時間,需要同步到該命令所對應的 redisCommand 結構中。修改 milliseconds 屬性的值,同時將 redisCommand 結構中的 calls 計數器的值加 1。

​ (2)如果這臺服務器啟動了慢查詢日誌功能,那麼慢查詢日誌模塊會判斷是否需要為剛執行完的命令添加一條慢查詢日誌。

​ (3)如果這臺服務器啟用了 AOF 持久化功能,那麼 AOF 持久化模塊會將這條執行完的命令請求寫入 AOF 緩衝區裡。

​ (4)如果有其他從服務器正在同步備份當前這臺服務器的數據,那麼這臺服務器會將剛執行完的命令請求轉發給與它相連的從服務器。

​ 當服務器完成上述相關後續工作的處理之後,會調用命令回覆處理器,此時客戶端的套接字變為可寫狀態。服務器調用命令回覆處理器將保存在客戶端輸出緩衝區中的協議格式的返回結果發送給客戶端

​ 當命令回覆處理器將返回結果成功發送給客戶端之後,它會刪除客戶端狀態的輸出緩衝區,為下一條命令請求的執行騰出空間

第五步 客戶端接收到服務器返回的命令結果 OK,然後將這個結果展示給用戶。


分享到:


相關文章: