推薦學習:Redis視訊教學
#Redis伺服器實際使用的是惰性刪除和定期刪除兩種策略:透過配合使用這兩種刪除策略,伺服器可以很好地在合理使用CPU時間和避免浪費記憶體空間之間取得平衡。
惰性刪除策略對CPU時間來說是最友善的:程式只會在取出鍵時才對鍵進行過期檢查,這可以保證刪除過期鍵的操作只會在非做不可的情況下進行,並且刪除的目標僅限於當前處理的鍵,這個策略不會在刪除其他無關的過期鍵上花費任何CPU時間。
惰性刪除策略的缺點是,它對記憶體是最不友善的:如果一個鍵已經過期,而這個鍵又仍然保留在資料庫中,那麼只要這個過期鍵不被刪除,它所佔用的記憶體就不會釋放。
在使用惰性刪除策略時,如果資料庫中有非常多的過期鍵,而這些過期鍵又恰好沒有被存取到的話,那麼它們也許永遠也不會被刪除(除非使用者手動執行FLUSHDB ),我們甚至可以將這種情況看作是一種內存洩漏——無用的垃圾數據佔用了大量的內存,而伺服器卻不會自己去釋放它們,這對於運行狀態非常依賴於內存的Redis伺服器來說,肯定不是一個好消息。
舉個例子,對於一些和時間有關的數據,例如日誌(log),在某個時間點之後,對它們的訪問就會大大減少,甚至不再訪問,如果這類過期數據大量地積壓在資料庫中,使用者以為伺服器已經自動將它們刪除了,但實際上這些鍵仍然存在,而且鍵所佔用的記憶體也沒有釋放,那麼造成的後果肯定是非常嚴重的。
從上面對惰性刪除的討論來看,這刪除方式在單一使用時有明顯的缺陷:
##惰性刪除浪費太多內存,有內存洩漏的危險。定期刪除策略是前兩種策略的一種整合和折中:定期刪除策略每隔一段時間執行一次刪除過期鍵操作,並透過限制刪除操作執行的時長和頻率來減少刪除操作對CPU時間的影響。除此之外,透過定期刪除過期鍵,定期刪除策略有效地減少了因為過期鍵而帶來的記憶體浪費。定期刪除策略的困難是確定刪除操作執行的時長和頻率:如果刪除操作執行得太頻繁,或者執行的時間太長,定期刪除策略就會退化成定時刪除策略,以至於將CPU時間過度消耗在刪除過期鍵上面。 如果刪除操作執行得太少,或是執行的時間太短,定期刪除策略又會和惰性刪除策略一樣,出現浪費記憶體的情況。因此,如果採用定期刪除策略的話,伺服器必須根據情況,合理地設定刪除操作的執行時長和執行頻率。 惰性刪除策略過期鍵的惰性刪除策略由db.c/expireIfNeeded函數實現,所有讀寫資料庫的Redis指令在執行前都會呼叫expireIfNeeded函數對輸入鍵進行檢查: 如果輸入鍵已經過期,那麼expireIfNeeded函數將輸入鍵從資料庫中刪除。 如果輸入鍵未過期,那麼expireIfNeeded函數不做動作。 expireIfNeeded函數就像一個過濾器,它可以在命令真正執行之前,過濾掉過期的輸入鍵,從而避免命令接觸到過期鍵。 另外,因為每個被存取的鍵都可能因為過期而被expireIfNeeded函數刪除,所以每個指令的實作函數都必須能同時處理鍵存在以及鍵不存在這兩種情況:當鍵存在時,指令會依照鍵存在的情況執行。 當鍵不存在或鍵因為過期而被expireIfNeeded函數刪除時,指令會依照鍵不存在的情況執行。過期鍵的定期刪除策略由redis.c/activeExpireCycle函數實現,每當Redis的伺服器週期性操作redis.c/serverCron函數執行時,activeExpireCycle函數就會被調用,它在規定的時間內,分多次遍歷伺服器中的各個資料庫,從資料庫的expires字典中隨機檢查一部分鍵的過期時間,並刪除其中的過期鍵。
整個過程可以用偽代碼描述如下
#activeExpireCycle函數的工作模式可以總結如下:
函數每次執行時,都會從一定數量的資料庫中取出一定數量的隨機鍵進行檢查,並刪除其中的過期鍵。
全域變數current_db會記錄目前activeExpireCycle函數檢查的進度,並在下一次activeExpireCycle函數呼叫時,接著上一次的進度進行處理。比方說,如果當前activeExpireCycle函數在遍歷10號資料庫時回傳了,那麼下次activeExpireCycle函數執行時,將從11號資料庫開始尋找並刪除過期鍵。
隨著activeExpireCycle函數的不斷執行,伺服器中的所有資料庫都會被檢查一遍,這時函數將current_db變數重設為0,然後再次開始新一輪的檢查工作。
當伺服器運行在複製模式下時,從伺服器的過期鍵刪除動作由主伺服器控制:
主伺服器在刪除一個過期鍵之後,會顯式地向所有從伺服器發送一個DEL指令,告知從伺服器刪除這個過期鍵。
從伺服器在執行客戶端發送的讀取命令時,即使碰到過期鍵也不會將過期鍵刪除,而是繼續像處理未過期的鍵一樣來處理過期鍵
#從伺服器只有在接到主伺服器發送的DEL指令之後,才會刪除過期鍵。
透過由主伺服器來控制從伺服器統一刪除過期鍵,可以保證主從伺服器資料的一致性,也正是因為這個原因,當一個過期鍵仍然存在於主伺服器的資料庫時,這個過期鍵在從伺服器裡的複製品也會繼續存在。舉個例子,有一對主從伺服器,它們的資料庫中都保存著同樣的三個鍵message、xxx和yyy,其中message為過期鍵,如圖所示。
如果這時有客戶端向從伺服器發送命令GET message,那麼從伺服器將發現message鍵已經過期,但從伺服器並不會刪除message鍵,而是繼續將message鍵的值傳回給客戶端,就好像message鍵並沒有過期一樣
#假設在此之後,有客戶端向主伺服器發送命令GET message ,那麼主伺服器將發現鍵message已經過期:主伺服器會刪除message鍵,向客戶端返回空回复,並向從伺服器發送DEL message命令
從伺服器在接收到主伺服器發送的DEL message指令之後,也會從資料庫中刪除message鍵,在這之後,主從伺服器都不再儲存過期鍵message了
推薦學習:Redis影片教學
以上是Redis的過期鍵刪除策略原理說明的詳細內容。更多資訊請關注PHP中文網其他相關文章!