數據庫
服務器中的數據庫
redisClient切換數據庫
redis客戶端默認目標數據庫為0號數據庫,可以通過SELECT命令來切換目標數據庫。
客戶端狀態redisClient結構的db屬性記錄了客戶端當前的目標數據庫,這個屬性是指向redisdb結構的指針。
typedef struct redisClient{ //記錄客戶端當前正在使用的數據庫 redisDb *db; } redisClient;
數據庫鍵空間
Redis是一個鍵值對數據庫服務器,服務器中的每個數據庫都由一個redis.h/redisDb結構表示,其中redisDB的dict字典保存了數據庫中的所有鍵值對,我們將這個字典稱為鍵空間。
typedef struct redisDb{ // 數據庫鍵空間,保存着數據庫中的所有鍵值對 dict *dict } redisDb;
鍵空間和用戶所見的數據庫是直接對應的:
1)鍵空間的鍵也就是數據庫的鍵,每個鍵都是一個字符串對象。
2)鍵空間的值也就是數據庫的值,每個值可以是字符串對象,列表對象,哈希表對象,集合對象和有序集合對象中任意一種Redis對象。
e.g.
redis > SET message "hello world" ok redis > RPUSH alphabet "a" "b" "c" 3 redis > HSET book namr "Redis in Action" 1 redis > HSET book author "Josiah L. Carlson" 1 redis > HSET book publisher "Manning" 1
讀寫鍵空間時的維護操作
當使用Redis命令對數據庫進行讀寫時,服務器不僅對鍵空間執行指定的讀寫操作。還會執行一些額外的維護工作。
1)對一個鍵的讀取命中次數和未命中次數,在INFO stats命令的keyspace_hits屬性和keyspace_misses屬性中查看。
2)讀取一個鍵之后,服務器會更新鍵的LRU時間,這個值可以用於計算鍵的閑置時間。
3)讀取一個鍵發現鍵已經過期了,那么服務器會刪除這個過期鍵,然后才執行余下的其他操作。
4)如果有客戶端使用WATCH命令監視某個鍵,被修改之后會記為臟(dirty),讓事務程序注意到這修改。
5)每次修改一個鍵之后,都會對臟(dirty)鍵計數器的值增1,這個計數器會觸發服務器的持久化以及復制操作。
6)鍵的修改觸發數據庫通知功能。
設置鍵的生存時間或過期時間
保存過期鍵:
typedef struct redisDb{ //過期字典,保存着鍵的過期時間 dict *expires; } redisDb;
移除過期時間,計算並返回剩余生存時間,過期鍵的判定類似。
過期鍵刪除策略
三種策略;定時刪除,惰性刪除,定期刪除。
redis的過期刪除策略
Redis服務器實際使用的是惰性刪除和定期刪除兩種策略:通過配合使用這兩種刪除策略,服務器可以很好地合理使用CPU時間和避免浪費內存空間之間取得平衡。
懶性刪除策略的實現: 定期刪除策略的實現:
AOF,RDB和復制功能對過期鍵的處理
RDB持久化:
生成RDB文件將過濾過期鍵。
載入RDB文件,如果是主服務器模式運行,過濾過期鍵;如果是從服務器模式運行,則一並載入,主從數據同步會清空從節點數據,所以不會有影響。
AOF持久化:
同樣對寫AOF文件,會過濾過期鍵。
復制功能:
數據庫通知
總結
RDB持久化
RDB文件的創建和載入
保存RDB二進制文件,使用SAVE,BGSAVE,其中save命令會堵塞進程,而bgsave會啟動后台進程。如果啟動了AOF持久化,那優先載入AOF日志。
自動間隔性保存
#設置保存條件 save 900 1 服務器在900秒之內,對數據庫進行了至少1次修改 save 300 10 服務器在300秒之內,對數據庫進行了至少10次修改 save 60 10000 服務器在60秒之內,對數據庫進行了至少10000次修改 #實現 struct redisServer{ //計入了保存條件的數組 struct saveparam *saveparam //dirty修改計數器 表示服務器在上次保存后,對數據庫狀態共進行多少次修改 long long dirty //上一次執行保存的時間 time_t lastsave } struct saveparam { //秒數 time_t seconds; //修改數 int changes } #檢查保存條件是否滿足,則每隔100毫秒周期性執行ServerCron函數,遍歷條件數組saveparam,對滿足條件的數據庫,計數器置為0,並更新上次保存時間。
總結:
AOF持久化
與RDB持久化不同的是,aof持久化是通過寫命令來保存數據庫狀態,而RDB保存的是鍵值對。

AOF持久化實現
AOF持久化功能分為:命令追加,文件寫入,文件同步三個步驟。
命令追加:
struct redisServer{ //AOF緩沖區 寫命令按照一定格式會追加到緩沖區 sds aof_buf; }
AOF文件的寫入與同步:
def eventLoop(): while True : #處理文件事件,接受命令請求以及發送命令回復 processFileEvents() #處理時間事件 類似於ServerCron定期執行函數 processTimeEvents() #考慮是否將aof_buf中的內容寫入和保存到AOF文件里面,三個選項 flushAppendOnlyFile()
AOF文件的載入與數據還原
AOF文件重寫
Redis服務器可以創建一個新的AOF文件來替代現有的AOF文件,新舊兩個AOF文件所保存的數據庫狀態相同。
但新的AOF文件不包含冗余命令,所以體積相對較小。
AOF后台重寫:
為了解決數據不一致問題,Redis服務器設置了一個AOF重寫緩沖區,這個緩沖區在服務器創建子進程之后開始使用。
當子進程完成AOF重寫工作之后,會向父進程發送一個信號,父進程會調用一個信號處理函數並執行以下操作:
1)將AOF重寫緩沖區中所有內容寫入新AOF文件中,這時新AOF保存的數據庫狀態==服務器當前數據庫狀態;
2)對新的AOF文件改名,原子替換舊的AOF文件;
注意:只有信號處理函數執行時會對服務器進程造成堵塞,對性能造成的影響降到最低。
總結:
事件
文件事件:
I/O多路復用程序總是會將所有產生事件的套接字都放在一個隊列里面,並串行化地向文件事件分派器傳送套接字。
時間事件
服務器將所有時間事件都放在一個無序鏈表中,每當時間事件執行器運行時,它就會遍歷整個鏈表,查找所有已到達的時間事件,並調用相應的事件處理器。
Redis時間事件分為兩類:
定時事件:讓一段程序在指定的時間之后執行一次。
周期性事件:讓一段程序每隔指定時間就執行一次。
事件的調度與執行
總結:
客戶端
struct redisServer{ //一個保存所有client的鏈表 list *clients; }
客戶端屬性
typedef struct redisClient{ //套接字描述符 int fd; //名字 robj *name; //標志,記錄客戶端角色,以及目前所處的狀態 int flag; //輸入緩沖區 用於保存客戶端發出的命令請求 sds querybuf;
//其他 如命令參數,參數個數,輸出緩沖區,身份認證,時間 }redisClient;
客戶端的創建與關閉
當客戶端與服務器通過網絡建立連接時,服務器就會調用連接處理事件,為客戶端創建相應的客戶端狀態,並將新的客戶端狀態添加到服務器狀態結構clients鏈表的尾鏈。
偽客戶端:
Lua腳本的偽客戶端
AOF文件的偽客戶端
總結:
服務端