## 添加新日志
在每次執行命令的之前和之后, 程序都會記錄微秒格式的當前 UNIX 時間戳, 這兩個時間戳之間的差就是服務器執行命令所耗費的時長, 服務器會將這個時長作為參數之一傳給?`slowlogPushEntryIfNeeded`?函數, 而?`slowlogPushEntryIfNeeded`?函數則負責檢查是否需要為這次執行的命令創建慢查詢日志, 以下偽代碼展示了這一過程:
~~~
# 記錄執行命令前的時間
before = unixtime_now_in_us()
# 執行命令
execute_command(argv, argc, client)
# 記錄執行命令后的時間
after = unixtime_now_in_us()
# 檢查是否需要創建新的慢查詢日志
slowlogPushEntryIfNeeded(argv, argc, before-after)
~~~
`slowlogPushEntryIfNeeded`?函數的作用有兩個:
1. 檢查命令的執行時長是否超過?`slowlog-log-slower-than`?選項所設置的時間, 如果是的話, 就為命令創建一個新的日志, 并將新日志添加到?`slowlog`?鏈表的表頭。
2. 檢查慢查詢日志的長度是否超過?`slowlog-max-len`?選項所設置的長度, 如果是的話, 那么將多出來的日志從?`slowlog`?鏈表中刪除掉。
以下是?`slowlogPushEntryIfNeeded`?函數的實現代碼:
~~~
void slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration) {
// 慢查詢功能未開啟,直接返回
if (server.slowlog_log_slower_than < 0) return;
// 如果執行時間超過服務器設置的上限,那么將命令添加到慢查詢日志
if (duration >= server.slowlog_log_slower_than)
// 新日志添加到鏈表表頭
listAddNodeHead(server.slowlog,slowlogCreateEntry(argv,argc,duration));
// 如果日志數量過多,那么進行刪除
while (listLength(server.slowlog) > server.slowlog_max_len)
listDelNode(server.slowlog,listLast(server.slowlog));
}
~~~
函數中的大部分代碼我們已經介紹過了, 唯一需要說明的是?`slowlogCreateEntry`?函數: 該函數根據傳入的參數, 創建一個新的慢查詢日志, 并將?`redisServer.slowlog_entry_id`?的值增一。
舉個例子, 假設服務器當前保存的慢查詢日志如圖 23-2 所示, 如果我們執行以下命令:
~~~
redis> EXPIRE msg 10086
(integer) 1
~~~
服務器在執行完這個?EXPIRE?命令之后, 就會調用?`slowlogPushEntryIfNeeded`?函數, 函數將為?EXPIRE?命令創建一條?`id`?為?`6`?的慢查詢日志, 并將這條新日志添加到?`slowlog`?鏈表的表頭, 如圖 23-3 所示。

注意, 除了?`slowlog`?鏈表發生了變化之外,?`slowlog_entry_id`?的值也從?`6`?變為?`7`?了。
之后,?`slowlogPushEntryIfNeeded`?函數發現, 服務器設定的最大慢查詢日志數目為?`5`?條, 而服務器目前保存的慢查詢日志數目為?`6`?條, 于是服務器將?`id`?為?`1`?的慢查詢日志刪除, 讓服務器的慢查詢日志數量回到設定好的?`5`?條。
刪除操作執行之后的服務器狀態如圖 23-4 所示。

- 介紹
- 前言
- 致謝
- 簡介
- 第一部分:數據結構與對象
- 簡單動態字符串
- SDS 的定義
- SDS 與 C 字符串的區別
- SDS API
- 重點回顧
- 參考資料
- 鏈表
- 鏈表和鏈表節點的實現
- 鏈表和鏈表節點的 API
- 重點回顧
- 字典
- 字典的實現
- 哈希算法
- 解決鍵沖突
- rehash
- 漸進式 rehash
- 字典 API
- 重點回顧
- 跳躍表
- 跳躍表的實現
- 跳躍表 API
- 重點回顧
- 整數集合
- 整數集合的實現
- 升級
- 升級的好處
- 降級
- 整數集合 API
- 重點回顧
- 壓縮列表
- 壓縮列表的構成
- 壓縮列表節點的構成
- 連鎖更新
- 壓縮列表 API
- 重點回顧
- 對象
- 對象的類型與編碼
- 字符串對象
- 列表對象
- 哈希對象
- 集合對象
- 有序集合對象
- 類型檢查與命令多態
- 內存回收
- 對象共享
- 對象的空轉時長
- 重點回顧
- 第二部分:單機數據庫的實現
- 數據庫
- 數據庫鍵空間
- 重點回顧
- RDB 持久化
- RDB 文件結構
- 重點回顧
- AOF 持久化
- AOF 持久化的實現
- 重點回顧
- 事件
- 文件事件
- 重點回顧
- 參考資料
- 客戶端
- 客戶端屬性
- 重點回顧
- 服務器
- 命令請求的執行過程
- 重點回顧
- 第三部分:多機數據庫的實現
- 復制
- 舊版復制功能的實現
- 重點回顧
- Sentinel
- 啟動并初始化 Sentinel
- 重點回顧
- 參考資料
- 集群
- 節點
- 重點回顧
- 第四部分:獨立功能的實現
- 發布與訂閱
- 頻道的訂閱與退訂
- 重點回顧
- 參考資料
- 事務
- 事務的實現
- 重點回顧
- Lua 腳本
- 創建并修改 Lua 環境
- 重點回顧
- 排序
- SORT <key> 命令的實現
- 重點回顧
- 二進制位數組
- GETBIT 命令的實現
- 重點回顧
- 慢查詢日志
- 慢查詢記錄的保存
- 慢查詢日志的閱覽和刪除
- 添加新日志
- 重點回顧
- 監視器
- 成為監視器
- 向監視器發送命令信息
- 重點回顧
- 源碼、相關資源和勘誤