前言
在使用redis時,我們會遇到一個問題,數據刪除后,數據量已經不大了,但是使用top命令查看,還會發(fā)現redis占用了很對內存。實際上,因為數據刪除后,redis釋放內存由內存分配器管理,不會立刻返回給操作系統(tǒng)。所以,操作系統(tǒng)仍然記錄著給redis分配了大量的內存
這往往會伴隨一個潛在的風險點:Redis 釋放的內存空間可能并不是連續(xù)的,那么,這些不連續(xù)的內存空間很有可能處于一種閑置的狀態(tài)。這就會導致一個問題:雖然有空閑空間,Redis 卻無法用來保存數據,不僅會減少 Redis 能夠實際保存的數據量,還會降低 Redis 運行機器的成本回報率。
什么是內存碎片
通常情況下,內存空間利用率低,往往是因為操作系統(tǒng)發(fā)生了比較嚴重的內存碎片,那么什么是內存碎片呢?可以將內存看成是高鐵上的作為,連續(xù)的空間相當于連座,內存碎片可以看成一個個零散的作為,如果你是3個人出行,火車上沒有三個座位連著的,那么你就沒法買到合適的作為,可能需要換一輛車

內存類似,如果需要申請一個N字節(jié)的連續(xù)空間,但是沒有這么大的連續(xù)空間,那么,這些剩余空間就是內存碎片,redis內存碎片是什么原因導致的呢,了解了原因才有可能比較好的解決
內存碎片形成的原因
一般來說內存碎片形成的原因有兩個,內因是操作系統(tǒng)的內存分配機制,外因是redis的負載特征
內因:內存分配器策略
內存分配器的分配策略就決定了操作系統(tǒng)無法做到“按需分配”。這是因為,內存分配器一般是按固定大小來分配內存,而不是完全按照應用程序申請的內存空間大小給程序分配。
Redis 可以使用 libc、jemalloc、tcmalloc 多種內存分配器來分配內存,默認使用 jemalloc。接下來,我就以 jemalloc 為例,來具體解釋一下。其他分配器也存在類似的問題。
jemalloc 的分配策略之一,是按照一系列固定的大小劃分內存空間,例如 8 字節(jié)、16 字節(jié)、32 字節(jié)、48 字節(jié),…, 2KB、4KB、8KB 等。當程序申請的內存最接近某個固定值時,jemalloc 會給它分配相應大小的空間。
外因:鍵值對大小不一樣和刪改操作
redis通常作為公共緩存和鍵值數據庫對外提供服務,所以對于不同大小的數據,redis申請內存空間大小不一,這是一個外因。
因為內存分配是按照固定大小分配,所以內存空間一般都會比申請的空間大一些,所以本身就會有一些內存碎片,降低內存空間存儲效率。

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

一開始,應用 A、B、C、D 分別保存了 3、1、2、4 字節(jié)的數據,并占據了相應的內存空間。然后,應用 D 刪除了 1 個字節(jié),這個 1 字節(jié)的內存空間就空出來了。緊接著,應用 A 修改了數據,從 3 字節(jié)變成了 4 字節(jié)。為了保持 A 數據的空間連續(xù)性,操作系統(tǒng)就需要把 B 的數據拷貝到別的空間,比如拷貝到 D 剛剛釋放的空間中。此時,應用 C 和 D 也分別刪除了 2 字節(jié)和 1 字節(jié)的數據,整個內存空間上就分別出現了 2 字節(jié)和 1 字節(jié)的空閑碎片。如果應用 E 想要一個 3 字節(jié)的連續(xù)空間,顯然是不能得到滿足的。因為,雖然空間總量夠,但卻是碎片空間,并不是連續(xù)的。
好了,到這里,我們就知道了造成內存碎片的內外因素,其中,內存分配器策略是內因,而 Redis 的負載屬于外因,包括了大小不一的鍵值對和鍵值對修改刪除帶來的內存空間變化。
如何判斷是否有內存碎片
Redis 是內存數據庫,內存利用率的高低直接關系到 Redis 運行效率的高低。為了讓用戶能監(jiān)控到實時的內存使用情況,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 是操作系統(tǒng)實際分配給 Redis 的物理內存空間,里面就包含了碎片;而 used_memory 是 Redis 為了保存數據實際申請使用的空間。
我簡單舉個例子。例如,Redis 申請使用了 100 字節(jié)(used_memory),操作系統(tǒng)實際分配了 128 字節(jié)(used_memory_rss),此時,mem_fragmentation_ratio 就是 1.28。
那么,知道了這個指標,我們該如何使用呢?在這兒,我提供一些經驗閾值:
- mem_fragmentation_ratio大于1小于1.5。這種情況是合理的。這是因為,剛才我介紹的那些因素是難以避免的。畢竟,內因的內存分配器是一定要使用的,分配策略都是通用的,不會輕易修改;而外因由 Redis 負載決定,也無法限制。所以,存在內存碎片也是正常的。
- mem_fragmentation_ratio大于1.5。 這表明內存碎片率已經超過了 50%。一般情況下,這個時候,我們就需要采取一些措施來降低內存碎片率了。
如何清理內存碎片
當 Redis 發(fā)生內存碎片后,一個“簡單粗暴”的方法就是重啟redis實例,當然這并不是一個優(yōu)雅的方法,重啟會帶來一些問題
- 如果數據沒有持久化,那么數據會丟失
- 如果數據持久化了,我們需要通過AOF或RDB進行恢復,恢復時長取決于AOF或RDB的大小,如果只有一個實例,在恢復階段無法提供服務。
幸運的是,從 4.0-RC3 版本以后,Redis 自身提供了一種內存碎片自動清理的方法,我們先來看這個方法的基本機制。還是通過一張圖來看下

在進行碎片清理前,這段 10 字節(jié)的空間中分別有 1 個 2 字節(jié)和 1 個 1 字節(jié)的空閑空間,只是這兩個空間并不連續(xù)。操作系統(tǒng)在清理碎片時,會先把應用 D 的數據拷貝到 2 字節(jié)的空閑空間中,并釋放 D 原先所占的空間。然后,再把 B 的數據拷貝到 D 原來的空間中。這樣一來,這段 10 字節(jié)空間的最后三個字節(jié)就是一塊連續(xù)空間了。到這里,碎片清理結束。
需要注意:碎片清理事由代價的,操作系統(tǒng)需要把多份數據拷貝到新位置,把原有空間釋放出來,這會帶來時間開銷。因為 Redis 是單線程,在數據拷貝時,Redis 只能等著,這就導致 Redis 無法及時處理請求,性能就會降低。而且,有的時候,數據拷貝還需要注意順序,就像剛剛說的清理內存碎片的例子,操作系統(tǒng)需要先拷貝 D,并釋放 D 的空間后,才能拷貝 B。這種對順序性的要求,會進一步增加 Redis 的等待時間,導致性能降低。
那么,有什么辦法可以盡量緩解這個問題嗎?這就要提到,Redis 專門為自動內存碎片清理功機制設置的參數了。我們可以通過設置參數,來控制碎片清理的開始和結束時機,以及占用的 CPU 比例,從而減少碎片清理對 Redis 本身請求處理的性能影響。
首先,Redis 需要啟用自動內存碎片清理,可以把 activedefrag 配置項設置為 yes,命令如下:
config set activedefrag yes
這個命令只是啟用了自動清理功能,但是,具體什么時候清理,會受到下面這兩個參數的控制。這兩個參數分別設置了觸發(fā)內存清理的一個條件,如果同時滿足這兩個條件,就開始清理。在清理的過程中,只要有一個條件不滿足了,就停止自動清理。
- active-defrag-ignore-bytes 100mb:表示內存碎片數量達到100MB時,開始清理
- active-defrag-threshold-lower 10:表示內存碎片空間占操作系統(tǒng)給redis分配空間的10%時開始清理
為了盡可能減少碎片清理對 Redis 正常請求處理的影響,自動內存碎片清理功能在執(zhí)行時,還會監(jiān)控清理操作占用的 CPU 時間,而且還設置了兩個參數,分別用于控制清理操作占用的 CPU 時間比例的上、下限,既保證清理工作能正常進行,又避免了降低 Redis 性能。這兩個參數具體如下:
- active-defrag-cycle-min 25:表示自動清理過程cpu時間筆記不低于25%,保證清理能正常開展
- active-defrag-cycle-max 75:表示自動清理過程所用 CPU 時間的比例不高于 75%,一旦超過,就停止清理,從而避免在清理時,大量的內存拷貝阻塞 Redis,導致響應延遲升高。
自動內存碎片清理機制在控制碎片清理啟停的時機上,既考慮了碎片的空間占比、對 Redis 內存使用效率的影響,還考慮了清理機制本身的 CPU 時間占比、對 Redis 性能的影響。而且,清理機制還提供了 4 個參數,讓我們可以根據實際應用中的數據量需求和性能要求靈活使用,建議你在實踐中好好地把這個機制用起來。
總結
到此這篇關于redis內存空間效率問題的文章就介紹到這了,更多相關redis內存空間效率內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- redis 限制內存使用大小的實現
- redis 使用lettuce 啟動內存泄漏錯誤的解決方案
- 淺談內存耗盡后Redis會發(fā)生什么
- 一次關于Redis內存詭異增長的排查過程實戰(zhàn)記錄
- 淺談redis內存數據的持久化方式
- 內存型數據庫Redis持久化小結
- 降低PHP Redis內存占用
- Redis教程(十四):內存優(yōu)化介紹
- 詳解Redis瘦身指南