Redis(三):Redis的內存淘汰機制與持久化機制

一:Redis的內存淘汰機制

 

redis 設置過期時間

Redis中有個設置時間過期的功能,即對存儲在 redis 數據庫中的值可以設置一個過期時間。作爲一個緩存數據庫,這是非常實用的。如我們一般項目中的 token 或者一些登錄信息,尤其是短信驗證碼都是有時間限制的,按照傳統的數據庫處理方式,一般都是自己判斷過期,這樣無疑會嚴重影響項目性能。

我們 set key 的時候,都可以給一個 expire time,就是過期時間,通過過期時間我們可以指定這個 key 可以存活的時間。

如果假設你設置了一批 key 只能存活1個小時,那麼接下來1小時後,redis是怎麼對這批key進行刪除的?

127.0.0.1:6379> mget aaa
1) "aaa"
127.0.0.1:6379> expire aaa 5
(integer) 1
127.0.0.1:6379> mget aaa
1) (nil)

定期刪除+惰性刪除。

通過名字大概就能猜出這兩個刪除方式的意思了。

  • 定期刪除:

redis默認是每隔 100ms 就隨機抽取一些設置了過期時間的key,檢查其是否過期,如果過期就刪除。注意這裏是隨機抽取的。爲什麼要隨機呢?你想一想假如 redis 存了幾十萬個 key ,每隔100ms就遍歷所有的設置過期時間的 key 的話,就會給 CPU 帶來很大的負載!

  • 惰性刪除 :

定期刪除可能會導致很多過期 key 到了時間並沒有被刪除掉。所以就有了惰性刪除。假如你的過期 key,靠定期刪除沒有被刪除掉,還停留在內存裏,除非你的系統去查一下那個 key,纔會被redis給刪除掉。這就是所謂的惰性刪除,也是夠懶的哈!

 

但是僅僅通過設置過期時間還是有問題的。我們想一下:如果定期刪除漏掉了很多過期 key,然後你也沒及時去查,也就沒走惰性刪除,此時會怎麼樣?如果大量過期key堆積在內存裏,導致redis內存塊耗盡了。怎麼解決這個問題呢? redis 內存淘汰機制。

 

redis 提供 6種數據淘汰策略:

 

  1. volatile-lru:從已設置過期時間的數據集(server.db[i].expires)中挑選最近最少使用的數據淘汰
  2. volatile-ttl:從已設置過期時間的數據集(server.db[i].expires)中挑選將要過期的數據淘汰
  3. volatile-random:從已設置過期時間的數據集(server.db[i].expires)中任意選擇數據淘汰
  4. allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key(這個是最常用的
  5. allkeys-random:從鍵空間數據集(server.db[i].dict)中任意選擇數據淘汰
  6. no-eviction(音標[ɪˈvɪkʃn]):禁止驅逐數據,也就是說當內存不足以容納新寫入數據時,新寫入操作會報錯。這個應該沒人使用吧!

4.0版本後增加以下兩種:

  1. volatile-lfu:從已設置過期時間的數據集(server.db[i].expires)中挑選最不經常使用的數據淘汰
  2. allkeys-lfu:當內存不足以容納新寫入數據時,在鍵空間中,移除最不經常使用的key

 

備註: 關於 redis 設置過期時間以及內存淘汰機制,我這裏只是簡單的總結一下,後面會專門寫一篇文章來總結!

 

二:redis 持久化機制(怎麼保證 redis 掛掉之後再重啓數據可以進行恢復)

 

很多時候我們需要持久化數據也就是將內存中的數據寫入到硬盤裏面,大部分原因是爲了之後重用數據(比如重啓機器、機器故障之後恢復數據),或者是爲了防止系統故障而將數據備份到一個遠程位置。

Redis不同於Memcached的很重一點就是,Redis支持持久化,而且支持兩種不同的持久化操作。Redis的一種持久化方式叫快照(snapshotting,RDB),另一種方式是隻追加文件(append-only file,AOF)。這兩種方法各有千秋,下面我會詳細這兩種持久化方法是什麼,怎麼用,如何選擇適合自己的持久化方法。

 

快照(snapshotting)持久化(RDB)

 

Redis可以通過創建快照來獲得存儲在內存裏面的數據在某個時間點上的副本。Redis創建快照之後,可以對快照進行備份,可以將快照複製到其他服務器從而創建具有相同數據的服務器副本(Redis主從結構,主要用來提高Redis性能),還可以將快照留在原地以便重啓服務器的時候使用。

快照持久化是Redis默認採用的持久化方式,在redis.conf配置文件中默認有此下配置:

#每次有數據修改發生時都會寫入AOF文件,這樣會嚴重降低Redis的速度
appendfsync always  
#每秒鐘同步一次,顯示地將多個寫命令同步到硬盤 
appendfsync everysec 
#讓操作系統決定何時進行同步 
appendfsync no  

爲了兼顧數據和寫入性能,用戶可以考慮 appendfsync everysec選項 ,讓Redis每秒同步一次AOF文件,Redis性能幾乎沒受到任何影響。而且這樣即使出現系統崩潰,用戶最多隻會丟失一秒之內產生的數據。當硬盤忙於執行寫入操作的時候,Redis還會優雅的放慢自己的速度以便適應硬盤的最大寫入速度。

 

Redis 4.0 對於持久化機制的優化

 

Redis 4.0 開始支持 RDB 和 AOF 的混合持久化(默認關閉,可以通過配置項 aof-use-rdb-preamble 開啓)。

如果把混合持久化打開,AOF 重寫的時候就直接把 RDB 的內容寫到 AOF 文件開頭。這樣做的好處是可以結合 RDB 和 AOF 的優點, 快速加載同時避免丟失過多的數據。當然缺點也是有的, AOF 裏面的 RDB 部分是壓縮格式不再是 AOF 格式,可讀性較差。

 

補充內容:AOF 重寫

AOF重寫可以產生一個新的AOF文件,這個新的AOF文件和原有的AOF文件所保存的數據庫狀態一樣,但體積更小。

AOF重寫是一個有歧義的名字,該功能是通過讀取數據庫中的鍵值對來實現的,程序無須對現有AOF文件進行任何讀入、分析或者寫入操作。

在執行 BGREWRITEAOF 命令時,Redis 服務器會維護一個 AOF 重寫緩衝區,該緩衝區會在子進程創建新AOF文件期間,記錄服務器執行的所有寫命令。當子進程完成創建新AOF文件的工作之後,服務器會將重寫緩衝區中的所有內容追加到新AOF文件的末尾,使得新舊兩個AOF文件所保存的數據庫狀態一致。最後,服務器用新的AOF文件替換舊的AOF文件,以此來完成AOF文件重寫操作。

 

RDB 優缺點

 

  • RDB 會生成多個數據文件,每個數據文件都代表了某一個時刻中 redis 的數據,這種多個數據文件的方式,非常適合做冷備,可以將這種完整的數據文件發送到一些遠程的安全存儲上去,比如說 Amazon 的 S3 雲服務上去,在國內可以是阿里雲的 ODPS 分佈式存儲上,以預定好的備份策略來定期備份 redis 中的數據。
  • RDB 對 redis 對外提供的讀寫服務,影響非常小,可以讓 redis 保持高性能,因爲 redis 主進程只需要 fork 一個子進程,讓子進程執行磁盤 IO 操作來進行 RDB 持久化即可。
  • 相對於 AOF 持久化機制來說,直接基於 RDB 數據文件來重啓和恢復 redis 進程,更加快速。
  • 如果想要在 redis 故障時,儘可能少的丟失數據,那麼 RDB 沒有 AOF 好。一般來說,RDB 數據快照文件,都是每隔 5 分鐘,或者更長時間生成一次,這個時候就得接受一旦 redis 進程宕機,那麼會丟失最近 5 分鐘的數據。
  • RDB 每次在 fork 子進程來執行 RDB 快照數據文件生成的時候,如果數據文件特別大,可能會導致對客戶端提供的服務暫停數毫秒,或者甚至數秒。

 

AOF 優缺點

 

  • AOF 可以更好的保護數據不丟失,一般 AOF 會每隔 1 秒,通過一個後臺線程執行一次fsync操作,最多丟失 1 秒鐘的數據。
  • AOF 日誌文件以 append-only 模式寫入,所以沒有任何磁盤尋址的開銷,寫入性能非常高,而且文件不容易破損,即使文件尾部破損,也很容易修復。
  • AOF 日誌文件即使過大的時候,出現後臺重寫操作,也不會影響客戶端的讀寫。因爲在 rewrite log 的時候,會對其中的指令進行壓縮,創建出一份需要恢復數據的最小日誌出來。在創建新日誌文件的時候,老的日誌文件還是照常寫入。當新的 merge 後的日誌文件 ready 的時候,再交換新老日誌文件即可。
  • AOF 日誌文件的命令通過非常可讀的方式進行記錄,這個特性非常適合做災難性的誤刪除的緊急恢復。比如某人不小心用 flushall 命令清空了所有數據,只要這個時候後臺 rewrite 還沒有發生,那麼就可以立即拷貝 AOF 文件,將最後一條 flushall 命令給刪了,然後再將該 AOF 文件放回去,就可以通過恢復機制,自動恢復所有數據。
  • 對於同一份數據來說,AOF 日誌文件通常比 RDB 數據快照文件更大。
  • AOF 開啓後,支持的寫 QPS 會比 RDB 支持的寫 QPS 低,因爲 AOF 一般會配置成每秒 fsync 一次日誌文件,當然,每秒一次 fsync,性能也還是很高的。(如果實時寫入,那麼 QPS 會大降,redis 性能會大大降低)
  • 以前 AOF 發生過 bug,就是通過 AOF 記錄的日誌,進行數據恢復的時候,沒有恢復一模一樣的數據出來。所以說,類似 AOF 這種較爲複雜的基於命令日誌 / merge / 回放的方式,比基於 RDB 每次持久化一份完整的數據快照文件的方式,更加脆弱一些,容易有 bug。不過 AOF 就是爲了避免 rewrite 過程導致的 bug,因此每次 rewrite 並不是基於舊的指令日誌進行 merge 的,而是基於當時內存中的數據進行指令的重新構建,這樣健壯性會好很多。


RDB 和 AOF 到底該如何選擇

  • 不要僅僅使用 RDB,因爲那樣會導致你丟失很多數據;
  • 也不要僅僅使用 AOF,因爲那樣有兩個問題:第一,你通過 AOF 做冷備,沒有 RDB 做冷備來的恢復速度更快;第二,RDB 每次簡單粗暴生成數據快照,更加健壯,可以避免 AOF 這種複雜的備份和恢復機制的 bug;
  • redis 支持同時開啓開啓兩種持久化方式,我們可以綜合使用 AOF 和 RDB 兩種持久化機制,用 AOF 來保證數據不丟失,作爲數據恢復的第一選擇; 用 RDB 來做不同程度的冷備,在 AOF 文件都丟失或損壞不可用的時候,還可以使用 RDB 來進行快速的數據恢復。

 

你們的老婆來了:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章