- 服務器中的數據庫
- Redis服務器將所有數據庫都保存在redisServer.db數組中,而數據庫的數量由redisServer.dbnum屬性保存,默認爲16(默認創建16個數據庫)
- 切換數據庫
- 默認情況下,Redis客戶端的目標數據庫爲0號數據庫,但可以通過SELECT命令切換數據庫
- 數據庫鍵空間
- 數據庫主要由dict和expires兩個字典構成,dict字典保存了數據庫中的所有鍵值對,被稱爲鍵空間(key space),而expires字典負責保存鍵的過期時間
- 其他鍵空間操作:
- FLUSHDB命令:清空整個數據庫(通過刪除鍵空間所有鍵值對來實現)
- DBSIZE命令:返回數據庫鍵數量(返回鍵空間所有鍵值對來實現)
- 設置鍵的生存時間或過期時間
- 通過EXPIRE/PEXPIRE命令,客戶端可以以秒/毫秒爲數據庫中的某個鍵設置生存時間(Time To Live,TTL),時間耗盡時,自動刪除
- 客戶端可以通過EXPIREAT/PEXPIREAT命令,以秒/毫秒爲數據庫中的某個鍵設置過期時間,到期時,自動刪除
- TTL/PTTL命令接受一個帶有生存時間或者過期時間的鍵,返回這個鍵的剩餘生存時間
最終EXPIRE、PEXPIRE、EXPIREAT都會轉換爲PEXPIREAT命令來執行
-
- 保存過期時間:expires字典的鍵指向數據庫中的某個鍵,值記錄了該鍵的過期時間
- 移除過期時間:PERSIST命令可以移除一個鍵的過期時間
- 過期鍵的判定:二步驟:
- 檢查過期字典是否有給定鍵:如果有,則取過期時間
- 檢查當前UNIX時間戳是否大於鍵的過期時間:如果是,那麼鍵已過期;否則,鍵未過期
- 過期鍵刪除策略*
- 定時刪除(主動刪除策略):在設置鍵的過期時間的同時,創建一個定時器,讓定時器在鍵的過期時間來臨時,立即執行對鍵的刪除操作
- 優點:對內存友好,通過定時器,可以讓過期鍵儘快刪除並釋放內存
- 缺點:佔用過多CPU時間,影響服務器的響應時間和吞吐量
- 惰性刪除(被動刪除策略):放任鍵過期不管,但是每次從鍵空間中獲取鍵時,都檢查取得的鍵是否過期,如果過期的話,就刪除該鍵,如果沒有過期,就返回該鍵
- 優點:對CPU時間友好,不會再刪除其它無關的過期鍵上花費任何CPU時間
- 缺點:浪費太多內存,有內存泄漏的危險
- 定期刪除(主動刪除策略):每隔一段時間,程序就對數據庫進行一次檢查,刪除裏面的過期鍵。至於要刪除多少過期鍵,要檢查多少個數據庫,由算法決定
- 優點(該策略是前兩種策略的一種整合和折中):
- 通過限制刪除操作執行的時長和頻率來減少刪除操作對CPU時間的影響
- 通過定期刪除過期鍵,有效減少了因爲過期鍵而帶來的內存浪費
- 難點(確定刪除操作執行的時長和頻率):
- 如果刪除操作太頻繁或執行時間過長,定期刪除策略就會退化成定時刪除,過多佔用CPU時間
- 如果刪除操作太少或執行時間太短,定期刪除策略優惠和惰性刪除一樣,出現浪費內存的情況
- 優點(該策略是前兩種策略的一種整合和折中):
- 定時刪除(主動刪除策略):在設置鍵的過期時間的同時,創建一個定時器,讓定時器在鍵的過期時間來臨時,立即執行對鍵的刪除操作
- Redis的過期鍵刪除策略**
- Redis通過配合使用惰性刪除和定期刪除兩種策略,更加合理的使用CPU時間和避免浪費內存空間之間取得平衡
- 惰性刪除策略的實現:
- 由db.c/expireIfNeeded函數實現,所有讀寫數據庫的redis命令在執行前都會調用expireIfNeeded函數對輸入鍵進行檢查
- 輸入鍵已過期則刪除鍵
- 未過期則執行實際的命令流程
- expireIfNeeded函數就像是一個過濾器,他可以在命令真正執行之前,過濾掉過期的輸入鍵,從而避免命令接觸過期鍵
- 因爲每個被訪問的鍵都可能因爲過期而被expireIfNeeded函數,所以每個命令的實現函數都必須能同時處理鍵存在以及鍵不存在這兩種情況
- 鍵存在時,命令按照鍵存在的情況執行
- 不存在或被expireIfNeeded函數刪除時,按不存在情況執行
- 由db.c/expireIfNeeded函數實現,所有讀寫數據庫的redis命令在執行前都會調用expireIfNeeded函數對輸入鍵進行檢查
-
- 定期刪除策略的實現
- 過期鍵的定期刪除策略有redis.c/activeExpireCycle函數實現,每當redis的服務器週期性操作redis.c/serverCron函數執行時,activeExpireCycle函數就會被調用,他在規定的時間內,分多次遍歷服務器的各個數據庫,從數據庫的expires字典中隨機檢查一部分鍵的過期時間,並刪除其中的過期鍵
- 定期刪除策略的實現
- AOF、RDB和複製功能對過期鍵的處理
- 生成RDB文件時:執行SAVE命令或者BGSAVE命令創建一個新的RDB文件時,程序會對數據庫中的鍵進行檢查,已過期的鍵不會保存到新建的RDB文件中
- 載入RDB文件時
- 以主服模式運行,載入RDB文件時,程序會檢查文件中的鍵進,只有未過期的鍵會被載入數據庫
- 以從服模式運行,載入RDB文件時,文件中所有的鍵都會載入到數據庫中,不過,主從服務器數據同步時,從庫會被清空
- AOF文件寫入時
- 當過期鍵被刪除後,程序會向AOF文件追加(append)一條DEL命令,來顯示地記錄該鍵已被刪除
- AOF重寫時
- 程序會對數據庫中的鍵進行檢查,過期鍵不會被保存到重寫後的AOF文件中
- 複製:當服務器運行在複製模式下時,從服務器的過期鍵刪除動作由主服務器控制:
- 主服刪除一個過期鍵後,會顯示地向所有從服發送一個DEL命令,告知從服刪除這個過期鍵
- 從服在執行客戶端發送的讀命令時,即使碰到過期鍵也不會刪,會被當做正常鍵處理
- 從服只有接到主服的DEL命令後,纔會刪除過期鍵
- 數據庫通知
- Redis2.8版本新增功能,可以讓客戶端通過訂閱給定的頻道或者模式,來獲知數據庫中鍵的變化/命令執行情況
- RDB持久化(RDB簡介)*
- RDB持久化既可以手動執行,也可以根據服務器配置選項定期執行,該功能可以將某個時間點上的數據庫狀態存到一個RDB文件中,避免數據意外丟失
- RDB文件:
- 是一個經過壓縮的二進制文件
- 只要文件在,就可以還原生成RDB文件時的數據庫狀態(即使服務器停機/進程退出)
- RDB文件的創建與載入(RDB原理之一**)
- 生成RDB文件的兩個命令
- SAVE命令會阻塞服務器直到執行完成STW
- BGSAVE命令會派生出一個子進程,然後由子進程負責創建RDB文件,服務器進程(父進程)繼續處理命令請求;其中有幾個命令跟平常不大一樣
- SAVE命令被拒絕;避免兩個rdbSave調用
- 禁止兩個BGSAVE同時執行;避免產生競爭條件
- BGREWRITEAOF和BGSAVE命令不能同時執行:這兩個命令都由子進程執行,併發出兩個子進程,並且兩個子進程都同時執行大量磁盤寫入操作
- RDB的載入是在啓動時檢測到RDB文件的存在,就會自動載入RDB文件中(沒有專門用於載入的命令);載入時,會一直處於阻塞狀態,直到完成載入
- 創建RDB文件的實際工作由rdb.c/rdbSave函數完成;載入RDB文件的實際工作由rdb.c/rdbLoad函數完成;
- 生成RDB文件的兩個命令
- 自動間隔性保存(RDB原理之二**)
- 因爲BGSAVE命令可以在不阻塞服務器進程的情況下執行;所以Redis允許用戶通過設置服務器配置的save選項,讓服務器定期自動執行一次BGSAVE命令
- 舉例:設置保存條件:服務器在90秒內,對數據庫進行了至少一次修改;滿足條件時,執行BGSAVE
- RDB文件結構(RDB原理之三**)
REDIS |
db_version |
databases |
EOF |
check_sum |
-
- RDB文件最開頭是REDIS部分,長度5字節,保存“REDIS”五個字符;用於程序載入文件時,判斷是否爲RDB文件
- db_version長度爲4字節,記錄了RDB文件的版本號
- databases 包含數據庫,以及庫中鍵值對
- EOF常量長度爲1字節(類似於SDS結尾的/0),標誌RDB文件正文內容結束
- check_sum是一個8字節長的無符號整數,保存着一個校驗和;這個校驗和是程序通過前四部分的內容計算得出;載入RDB文件時,會將載入數據所計算出的校驗和與check_sum所記錄的校驗和對比,檢查RDB文件是否損壞/出錯