圖片來自《Redis設計與實現》
Redis數據庫
redis.server中redisServer.dbnum用於初始化生成多少個數據庫,默認
16個,
Select命令選擇數據庫
redis.client中redis.db指針指向當前正在使用的數據庫
1、過時策略
設置過時時間命令
expire如 expire <key> <ttl>
redisDb主要由dict和expires兩個字典組成:
dict 爲鍵空間字典,存放該數據庫中全部的鍵對象;expires參數稱爲過時字典,存放每一個鍵對應的過時時間
expires的鍵是一個指針,指向鍵空間中的某個鍵對象;值是long long類型的整數,存放過時時間(毫秒UNIX時間戳(chuo))
兩步判斷鍵是否過時:
(1)檢查給定鍵是否存在於過時字典;存在,則得到對應的時間戳
(2)獲取當前UNIX時間戳,二者進行比較,便可知道是否過時
三個過時鍵刪除策略:
1.
定時刪除:使用定時器(基於時間事件,無序鏈表,複雜度O(N),沒法高效處理大量時間事件),定時檢查鍵是否過時,對內存友好的策略
2.
惰性刪除:使用鍵時纔去檢查該鍵是否過時,對CPU友好的策略
3.
按期刪除:折中的策略,隨機檢查一部分鍵的過時時間並刪除其中過時的鍵,難點是肯定刪除執行的時長和頻率
Redis中採用 惰性刪除 和 按期刪除 配合使用
因爲兩種過時鍵刪除策略仍然可能會致使大量過時鍵堆積在內存,爲此,Redis引入的內存淘汰機制(淘汰策略共6種。最重要的是 allkeys-lru)
過時鍵對RDB、AOF持久化以及複製的影響
RDB:RDB持久化時,會檢查鍵是否過時;RDB載入時,主服務器會檢查鍵是否過時,從服務器忽略,等待主服務器同步時處理
AOF:AOF文件寫入時忽略,直到過時鍵被刪除,才向AOF文件追加一條刪除過時鍵的命令
AOF文件重寫時,會檢查鍵是否過時
複製:主服務器每刪除一個過時鍵,則顯示地向全部從服務器發送一個DEL命令,通知從服務器刪除過時鍵
(這個其實就是主從複製的同步流程?)
2、持久化
Redis的持久化分爲兩種:RDB持久化 和 AOF持久化
RDB持久化:
一個通過壓縮的二進制文件;以鍵值對形式存儲數據庫信息,不一樣的鍵值對使用不一樣的方式保存
兩個命令的區別:SAVE,BGSAVE
save:
阻塞式RDB持久化。服務器線程執行RDB持久化操做,期間不能處理任何命令請求
bgsave:
非阻塞式RDB持久化。
會建立一個子進程用於執行RDB持久化操做,服務器線程仍然能處理命令請求
RDB文件載入
無命令,服務器啓動時自動執行;若服務器開啓了AOF持久化功能, 會優先使用AOF文件來還原數據庫狀態
RDB自動間隔性保存
可配置服務器save選項,每隔一段時間自動執行一次bgsave命令
默認知足三個條件之一,則自動執行一次bgsave命令。條件存儲在 saveparams 參數中
除saveparams數組外,還存儲
dirty計數器 和
lastsave屬性;dirty用於計算累積操做次數,lastsave用於和當前時間戳比較,計算通過的時間
三個條件:
900 1;300 10;60 10000
Redis的週期性操做函數 serverCron 默認每間隔100ms執行一次,用於對正運行的服務器維護,其中就包括檢查參數條件是否知足
AOF持久化:
經過保存Redis命令來保存數據庫信息
以Redis命令請求協議格式保存,Append Only File
三步實現AOF持久化:
(1)命令追加:每執行一個命令,就將命令追加到
aof_buf緩衝區(常量值大小64)末尾
(2)命令寫入:服務器每次結束一個事件循環前,都會調用flushAppendOnlyFile函數,考慮是否將緩衝區數據寫入AOF文件
(3)命令同步:與flushAppendOnlyFile函數的
appendfsync 參數決定
always:緩衝區寫入並同步到AOF文件
everysec:每秒同步一次
no:只寫入,由操做系統決定同步
AOF的載入還原:
(1)建立一個不帶網絡鏈接的僞客戶端(用於執行AOF文件中的Redis命令,由於命令只能在客戶端中執行)
(2)從AOF文件中分析並讀取出一條命令
(3)僞客戶端執行命令
(4)循環執行2,3;直到AOF文件中的命令執行完畢
AOF的文件重寫: bgrewriteaof
爲何須要:爲了解決隨時間和操做的增加,AOF文件的膨脹問題,處理AOF文件中命令冗餘
實現理念:不是讀取原有AOF文件內容,而是直接讀取當前數據庫狀態,而後用一條(或多條,儘可能少的)命令去記錄鍵值對
實現過程:
1.建立一個
子進程後臺執行重寫 --- 避免使用鎖的狀況下,保證數據安全;且不阻塞服務器進程
2.使用了AOF重寫緩衝區,服務器每執行一個命令,都將命令同時追加到AOF緩衝區和AOF重寫緩衝區
3.重寫工做完成後,子進程返回一個信號,父進程調用信號處理函數
(1)將AOF重寫緩衝區的內容寫入新AOF文件中,使新AOF文件與數據庫狀態一致
(2)對新AOF文件更名,原子性地覆蓋現有AOF文件,完成新舊AOF文件交替
注意:調用信號處理函數階段,服務器進程阻塞。(
重寫過程當中就這一處阻塞)
RDB與AOF兩種持久化方式的比較
(1)RDB文件小,恢復快;可是沒法保存最近一次快照後的數據,即會丟失這部分數據
(2)AOF文件可讀性高,數據不易丟失;可是文件體積大,恢復時間長
Redis 4.0 開始支持 RDB 和 AOF 的混合持久化
3、事件
Redis 服務器是一個事件驅動程序
單線程運行,
I/O多路複用來監聽多個套接字;有兩種事件(文件事件和時間事件)
文件事件:對套接字進行操做的抽象;有可讀事件、可寫事件;文件事件擁有基於Reactor模式的文件事件處理器。
文件事件處理器: 套接字 + I/O多路複用程序 + 文件事件分派器 + 事件處理器
常見事件處理器:鏈接應答處理器,命令請求處理器,命令回覆處理器。(依次爲鏈接、接收命令、處理命令回覆)
時間事件:給定的時間點執行類操做的抽象;有定時事件、週期性事件redis
時間事件實現: Redis 將全部時間事件都放在一個無序鏈表(不按when屬性大小排序)中,經過遍歷整個鏈表查找出已到達的時間事件,並調用相應的事件處理器。數據庫
時間事件:三個參數(id,when,timeProc);典型事件:serverCron數組
事件調度與執行
def aeProcessEvents():
# 獲取到達時間離當前時間最接近的時間事件
time_event = aeSearchNearestTimer()
# 計算最接近的時間事件距離到達還有多少毫秒
remaind_ms = time_event.when - unix_ts_now()
# 若是事件已到達,那麼 remaind_ms 的值可能爲負數,將它設爲 0
if remaind_ms < 0:
remaind_ms = 0
# 根據 remaind_ms 的值,建立 timeval
timeval = create_timeval_with_ms(remaind_ms)
# 阻塞並等待文件事件產生,最大阻塞時間由傳入的 timeval 決定
aeApiPoll(timeval)
# 處理全部已產生的文件事件
procesFileEvents()
# 處理全部已到達的時間事件
processTimeEvents()