【Redis】慢查詢日誌

慢查詢日誌是 Redis 提供的一個用於觀察系統性能的功能, 這個功能的實現非常簡單, 這裏我們也簡單地講解一下。

本章先介紹和慢查詢功能相關的數據結構和變量, 然後介紹 Redis 是如何記錄命令的執行時間, 以及如何爲執行超過限制事件的命令記錄慢查詢日誌的。

相關數據結構

每條慢查詢日誌都以一個 slowlog.h/slowlogEntry 結構定義:

typedef struct slowlogEntry {

    // 命令參數
    robj **argv;

    // 命令參數數量
    int argc;

    // 唯一標識符
    long long id;       /* Unique entry identifier. */

    // 執行命令消耗的時間,以納秒(1 / 1,000,000,000 秒)爲單位
    long long duration; /* Time spent by the query, in nanoseconds. */

    // 命令執行時的時間
    time_t time;        /* Unix time at which the query was executed. */

} slowlogEntry;

記錄服務器狀態的 redis.h/redisServer 結構裏保存了幾個和慢查詢有關的屬性:

struct redisServer {

    // ... other fields

    // 保存慢查詢日誌的鏈表
    list *slowlog;                  /* SLOWLOG list of commands */

    // 慢查詢日誌的當前 id 值
    long long slowlog_entry_id;     /* SLOWLOG current entry ID */

    // 慢查詢時間限制
    long long slowlog_log_slower_than; /* SLOWLOG time limit (to get logged) */

    // 慢查詢日誌的最大條目數量
    unsigned long slowlog_max_len;     /* SLOWLOG max number of items logged */

    // ... other fields
};

slowlog 屬性是一個鏈表, 鏈表裏的每個節點保存了一個慢查詢日誌結構, 所有日誌按添加時間從新到舊排序,新的日誌在鏈表的左端,舊的日誌在鏈表的右端。

slowlog_entry_id 在創建每條新的慢查詢日誌時增一,用於產生慢查詢日誌的 ID (這個 ID 在執行 SLOWLOG RESET 之後會被重置)。

slowlog_log_slower_than 是用戶指定的命令執行時間上限,執行時間大於等於這個值的命令會被慢查詢日誌記錄。

slowlog_max_len 慢查詢日誌的最大數量,當日志數量等於這個值時,添加一條新日誌會造成最舊的一條日誌被刪除。

下圖展示了一個 slowlog 屬性的實例:
在這裏插入圖片描述

慢查詢日誌的記錄

在每次執行命令之前, Redis 都會用一個參數記錄命令執行前的時間, 在命令執行完之後, 再計算一次當前時間, 然後將兩個時間值相減, 得出執行命令所耗費的時間值 duration , 並將 duration 傳給 slowlogPushEntryIfNeed 函數。

如果 duration 超過服務器設置的執行時間上限 server.slowlog_log_slower_than 的話, slowlogPushEntryIfNeed 就會創建一條新的慢查詢日誌, 並將它加入到慢查詢日誌鏈表裏。

可以用一段僞代碼來表示這個過程:

def execute_redis_command_with_slowlog():

    # 命令執行前的時間
    start = ustime()

    # 執行命令
    execute_command(argv, argc)

    # 計算命令執行所耗費的時間
    duration = ustime() - start

    if slowlog_is_enabled:
        slowlogPushEntryIfNeed(argv, argc, duration)

def slowlogPushEntryIfNeed(argv, argc, duration)

    # 如果執行命令耗費的時間超過服務器設置命令執行時間上限
    # 那麼創建一條新的 slowlog
    if duration > server.slowlog_log_slower_than:

        # 創建新 slowlog
        log = new slowlogEntry()

        # 設置各個域
        log.argv = argv
        log.argc = argc
        log.duration = duration
        log.id = server.slowlog_entry_id
        log.time = now()

        # 將新 slowlog 追加到日誌鏈表末尾
        server.slowlog.append(log)

        # 更新服務器 slowlog
        server.slowlog_entry_id += 1
慢查詢日誌的操作

針對慢查詢日誌有三種操作,分別是查看、清空和獲取日誌數量:

  • 查看日誌:在日誌鏈表中遍歷指定數量的日誌節點,複雜度爲 O(N) 。
  • 清空日誌:釋放日誌鏈表中的所有日誌節點,複雜度爲 O(N) 。
  • 獲取日誌數量:獲取日誌的數量等同於獲取 server.slowlog 鏈表的數量,複雜度爲 O(1) 。
小結

Redis 用一個鏈表以 FIFO 的順序保存着所有慢查詢日誌。
每條慢查詢日誌以一個慢查詢節點表示,節點中記錄着執行超時的命令、命令的參數、命令執行時的時間,以及執行命令所消耗的時間等信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章