redis內存空間效率問題的深入探究

前言

在使用redis時,我們會遇到一個問題,數據刪除後,數據量已經不大瞭,但是使用top命令查看,還會發現redis占用瞭很對內存。實際上,因為數據刪除後,redis釋放內存由內存分配器管理,不會立刻返回給操作系統。所以,操作系統仍然記錄著給redis分配瞭大量的內存

這往往會伴隨一個潛在的風險點:Redis 釋放的內存空間可能並不是連續的,那麼,這些不連續的內存空間很有可能處於一種閑置的狀態。這就會導致一個問題:雖然有空閑空間,Redis 卻無法用來保存數據,不僅會減少 Redis 能夠實際保存的數據量,還會降低 Redis 運行機器的成本回報率。

什麼是內存碎片

通常情況下,內存空間利用率低,往往是因為操作系統發生瞭比較嚴重的內存碎片,那麼什麼是內存碎片呢?可以將內存看成是高鐵上的作為,連續的空間相當於連座,內存碎片可以看成一個個零散的作為,如果你是3個人出行,火車上沒有三個座位連著的,那麼你就沒法買到合適的作為,可能需要換一輛車

內存類似,如果需要申請一個N字節的連續空間,但是沒有這麼大的連續空間,那麼,這些剩餘空間就是內存碎片,redis內存碎片是什麼原因導致的呢,瞭解瞭原因才有可能比較好的解決

內存碎片形成的原因

一般來說內存碎片形成的原因有兩個,內因是操作系統的內存分配機制,外因是redis的負載特征

內因:內存分配器策略

內存分配器的分配策略就決定瞭操作系統無法做到“按需分配”。這是因為,內存分配器一般是按固定大小來分配內存,而不是完全按照應用程序申請的內存空間大小給程序分配。

Redis 可以使用 libc、jemalloc、tcmalloc 多種內存分配器來分配內存,默認使用 jemalloc。接下來,我就以 jemalloc 為例,來具體解釋一下。其他分配器也存在類似的問題。

jemalloc 的分配策略之一,是按照一系列固定的大小劃分內存空間,例如 8 字節、16 字節、32 字節、48 字節,…, 2KB、4KB、8KB 等。當程序申請的內存最接近某個固定值時,jemalloc 會給它分配相應大小的空間。

外因:鍵值對大小不一樣和刪改操作

redis通常作為公共緩存和鍵值數據庫對外提供服務,所以對於不同大小的數據,redis申請內存空間大小不一,這是一個外因。

因為內存分配是按照固定大小分配,所以內存空間一般都會比申請的空間大一些,所以本身就會有一些內存碎片,降低內存空間存儲效率。

第二個外因是,這些數據會被刪除和修改,會導致空間空間擴充和釋放,具體來說,一方面,如果修改後的鍵值對變大或變小瞭,就需要占用額外的空間或者釋放不用的空間。另一方面,刪除的鍵值對就不再需要內存空間瞭,此時,就會把空間釋放出來,形成空閑空間

一開始,應用 A、B、C、D 分別保存瞭 3、1、2、4 字節的數據,並占據瞭相應的內存空間。然後,應用 D 刪除瞭 1 個字節,這個 1 字節的內存空間就空出來瞭。緊接著,應用 A 修改瞭數據,從 3 字節變成瞭 4 字節。為瞭保持 A 數據的空間連續性,操作系統就需要把 B 的數據拷貝到別的空間,比如拷貝到 D 剛剛釋放的空間中。此時,應用 C 和 D 也分別刪除瞭 2 字節和 1 字節的數據,整個內存空間上就分別出現瞭 2 字節和 1 字節的空閑碎片。如果應用 E 想要一個 3 字節的連續空間,顯然是不能得到滿足的。因為,雖然空間總量夠,但卻是碎片空間,並不是連續的。

好瞭,到這裡,我們就知道瞭造成內存碎片的內外因素,其中,內存分配器策略是內因,而 Redis 的負載屬於外因,包括瞭大小不一的鍵值對和鍵值對修改刪除帶來的內存空間變化。

如何判斷是否有內存碎片

Redis 是內存數據庫,內存利用率的高低直接關系到 Redis 運行效率的高低。為瞭讓用戶能監控到實時的內存使用情況,Redis 自身提供瞭 INFO 命令,可以用來查詢內存使用的詳細信息,命令如下:

INFO memory
# Memory
used_memory:1073741736
used_memory_human:1024.00M
used_memory_rss:1997159792
used_memory_rss_human:1.86G
…
mem_fragmentation_ratio:1.86O memory

INFO memory
# Memory
used_memory:1073741736
used_memory_human:1024.00M
used_memory_rss:1997159792
used_memory_rss_human:1.86G
…
mem_fragmentation_ratio:1.86

這裡有一個 mem_fragmentation_ratio 的指標,它表示的就是 Redis 當前的內存碎片率。那麼,這個碎片率是怎麼計算的呢?其實,就是上面的命令中的兩個指標 used_memory_rss 和 used_memory 相除的結果。

mem_fragmentation_ratio = used_memory_rss/ used_memory

used_memory_rss 是操作系統實際分配給 Redis 的物理內存空間,裡面就包含瞭碎片;而 used_memory 是 Redis 為瞭保存數據實際申請使用的空間。

我簡單舉個例子。例如,Redis 申請使用瞭 100 字節(used_memory),操作系統實際分配瞭 128 字節(used_memory_rss),此時,mem_fragmentation_ratio 就是 1.28。

那麼,知道瞭這個指標,我們該如何使用呢?在這兒,我提供一些經驗閾值:

  • mem_fragmentation_ratio大於1小於1.5。這種情況是合理的。這是因為,剛才我介紹的那些因素是難以避免的。畢竟,內因的內存分配器是一定要使用的,分配策略都是通用的,不會輕易修改;而外因由 Redis 負載決定,也無法限制。所以,存在內存碎片也是正常的。
  • mem_fragmentation_ratio大於1.5。 這表明內存碎片率已經超過瞭 50%。一般情況下,這個時候,我們就需要采取一些措施來降低內存碎片率瞭。

如何清理內存碎片

當 Redis 發生內存碎片後,一個“簡單粗暴”的方法就是重啟redis實例,當然這並不是一個優雅的方法,重啟會帶來一些問題

  • 如果數據沒有持久化,那麼數據會丟失
  • 如果數據持久化瞭,我們需要通過AOF或RDB進行恢復,恢復時長取決於AOF或RDB的大小,如果隻有一個實例,在恢復階段無法提供服務。

幸運的是,從 4.0-RC3 版本以後,Redis 自身提供瞭一種內存碎片自動清理的方法,我們先來看這個方法的基本機制。還是通過一張圖來看下

在進行碎片清理前,這段 10 字節的空間中分別有 1 個 2 字節和 1 個 1 字節的空閑空間,隻是這兩個空間並不連續。操作系統在清理碎片時,會先把應用 D 的數據拷貝到 2 字節的空閑空間中,並釋放 D 原先所占的空間。然後,再把 B 的數據拷貝到 D 原來的空間中。這樣一來,這段 10 字節空間的最後三個字節就是一塊連續空間瞭。到這裡,碎片清理結束。

需要註意:碎片清理事由代價的,操作系統需要把多份數據拷貝到新位置,把原有空間釋放出來,這會帶來時間開銷。因為 Redis 是單線程,在數據拷貝時,Redis 隻能等著,這就導致 Redis 無法及時處理請求,性能就會降低。而且,有的時候,數據拷貝還需要註意順序,就像剛剛說的清理內存碎片的例子,操作系統需要先拷貝 D,並釋放 D 的空間後,才能拷貝 B。這種對順序性的要求,會進一步增加 Redis 的等待時間,導致性能降低。

那麼,有什麼辦法可以盡量緩解這個問題嗎?這就要提到,Redis 專門為自動內存碎片清理功機制設置的參數瞭。我們可以通過設置參數,來控制碎片清理的開始和結束時機,以及占用的 CPU 比例,從而減少碎片清理對 Redis 本身請求處理的性能影響。

首先,Redis 需要啟用自動內存碎片清理,可以把 activedefrag 配置項設置為 yes,命令如下:

config set activedefrag yes

這個命令隻是啟用瞭自動清理功能,但是,具體什麼時候清理,會受到下面這兩個參數的控制。這兩個參數分別設置瞭觸發內存清理的一個條件,如果同時滿足這兩個條件,就開始清理。在清理的過程中,隻要有一個條件不滿足瞭,就停止自動清理。

  • active-defrag-ignore-bytes 100mb:表示內存碎片數量達到100MB時,開始清理
  • active-defrag-threshold-lower 10:表示內存碎片空間占操作系統給redis分配空間的10%時開始清理

為瞭盡可能減少碎片清理對 Redis 正常請求處理的影響,自動內存碎片清理功能在執行時,還會監控清理操作占用的 CPU 時間,而且還設置瞭兩個參數,分別用於控制清理操作占用的 CPU 時間比例的上、下限,既保證清理工作能正常進行,又避免瞭降低 Redis 性能。這兩個參數具體如下:

  • active-defrag-cycle-min 25:表示自動清理過程cpu時間筆記不低於25%,保證清理能正常開展
  • active-defrag-cycle-max 75:表示自動清理過程所用 CPU 時間的比例不高於 75%,一旦超過,就停止清理,從而避免在清理時,大量的內存拷貝阻塞 Redis,導致響應延遲升高。

自動內存碎片清理機制在控制碎片清理啟停的時機上,既考慮瞭碎片的空間占比、對 Redis 內存使用效率的影響,還考慮瞭清理機制本身的 CPU 時間占比、對 Redis 性能的影響。而且,清理機制還提供瞭 4 個參數,讓我們可以根據實際應用中的數據量需求和性能要求靈活使用,建議你在實踐中好好地把這個機制用起來。

總結

到此這篇關於redis內存空間效率問題的文章就介紹到這瞭,更多相關redis內存空間效率內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: