天天看點

Redis 删除資料後,為什麼記憶體占用率還是很高?

在使用 Redis 時,我們經常會遇到這樣一個問題:明明做了資料删除,資料量已經不大了,為什麼使用 top 指令檢視時,還會發現 Redis 占用了很多記憶體呢?

實際上,這是因為,當資料删除後,Redis 釋放的記憶體空間會由記憶體配置設定器管理,并不會立即傳回給作業系統。是以,作業系統仍然會記錄着給 Redis 配置設定了大量記憶體。

但是,這往往會伴随一個潛在的風險點:Redis 釋放的記憶體空間可能并不是連續的,那麼,這些不連續的記憶體空間很有可能處于一種閑置的狀态。這就會導緻一個問題:雖然有空閑空間,Redis 卻無法用來儲存資料,不僅會減少 Redis 能夠實際儲存的資料量,還會降低 Redis 運作機器的成本回報率。

是以,這節課,我就和你聊聊 Redis 的記憶體空間存儲效率問題,探索一下,為什麼資料已經删除了,但記憶體卻閑置着沒有用,以及相應的解決方案。什麼是記憶體碎片?

什麼是記憶體碎片?

通常情況下,記憶體空間閑置,往往是因為作業系統發生了較為嚴重的記憶體碎片。那麼,什麼是記憶體碎片呢?

為了友善你了解,我還是借助高鐵的車廂座位來進行解釋。假設一個車廂的座位總共有 60 個,現在已經賣了 57 張票,你和 2 個小夥伴要乘坐高鐵出門旅行,剛好需要三張票。不過,你們想要坐在一起,這樣可以在路上聊天。但是,在選座位時,你們卻發現,已經買不到連續的座位了。于是,你們隻好換了一趟車。這樣一來,你們需要改變出行時間,而且這趟車就空置了三個座位。

其實,這趟車的空座位是和你們的人數相比對的,隻是這些空座位是分散的,如下圖所示:

Redis 删除資料後,為什麼記憶體占用率還是很高?

我們可以把這些分散的空座位叫作“車廂座位碎片”,知道了這一點,作業系統的記憶體碎片就很容易了解了。雖然作業系統的剩餘記憶體空間總量足夠,但是,應用申請的是一塊連續位址空間的 N 位元組,但在剩餘的記憶體空間中,沒有大小為 N 位元組的連續空間了,那麼,這些剩餘空間就是記憶體碎片(比如上圖中的“空閑 2 位元組”和“空閑 1 位元組”,就是這樣的碎片)。

那麼,Redis 中的記憶體碎片是什麼原因導緻的呢?接下來,我帶你來具體看一看。我們隻有了解了記憶體碎片的成因,才能對症下藥,把 Redis 占用的記憶體空間充分利用起來,增加存儲的資料量。

記憶體碎片是如何形成的?

其實,記憶體碎片的形成有内因和外因兩個層面的原因。簡單來說,内因是作業系統的記憶體配置設定機制,外因是 Redis 的負載特征。

内因:記憶體配置設定器的配置設定政策

記憶體配置設定器的配置設定政策就決定了作業系統無法做到“按需配置設定”。這是因為,記憶體配置設定器一般是按固定大小來配置設定記憶體,而不是完全按照應用程式申請的記憶體空間大小給程式配置設定。

Redis 可以使用 libc、jemalloc、tcmalloc 多種記憶體配置設定器來配置設定記憶體,預設使用 jemalloc。接下來,我就以 jemalloc 為例,來具體解釋一下。其他配置設定器也存在類似的問題。

jemalloc 的配置設定政策之一,是按照一系列固定的大小劃分記憶體空間,例如 8 位元組、16 位元組、32 位元組、48 位元組,…, 2KB、4KB、8KB 等。當程式申請的記憶體最接近某個固定值時,jemalloc 會給它配置設定相應大小的空間。

這樣的配置設定方式本身是為了減少配置設定次數。例如,Redis 申請一個 20 位元組的空間儲存資料,jemalloc 就會配置設定 32 位元組,此時,如果應用還要寫入 10 位元組的資料,Redis 就不用再向作業系統申請空間了,因為剛才配置設定的 32 位元組已經夠用了,這就避免了一次配置設定操作。

但是,如果 Redis 每次向配置設定器申請的記憶體空間大小不一樣,這種配置設定方式就會有形成碎片的風險,而這正好來源于 Redis 的外因了。

比如說,應用 A 儲存 6 位元組資料,jemalloc 按配置設定政策配置設定 8 位元組。如果應用 A 不再儲存新資料,那麼,這裡多出來的 2 位元組空間就是記憶體碎片了,如下圖所示:

繼續閱讀