敗家玩意兒!Redis 竟然浪費了這麼多記憶體!

2022-03-17 13:44:12 字數 2910 閱讀 8527

作為記憶體資料庫,記憶體空間大小對於 redis 來說是至關重要的。記憶體越多,意味著儲存的資料也會越多。但是不知道你有沒有遇到過這樣的情況,明明空間很大,但是記憶體的使用卻不是很理想。

為什麼會出現這樣的情況呢?這期我們就來看看這個"詭異"的事件。

坐好了,準備發車!

圖注:思維導圖

首先想要知道 redis 記憶體的使用情況,我們就需要獲取相關的資訊。

這裡有乙個記憶體碎片率的名詞需要關注下,它可以用來表示當前的記憶體使用情況。

具體計算方式: 

對於記憶體碎片率,一般保持在1~1.5

了解了記憶體碎片率,那什麼是記憶體碎片呢?

定義是這樣的:由於一塊連續空閒的空間比所要申請的空間小,導致這塊空間不可用,對於記憶體整體來說就是記憶體碎片。

舉個例子:

假設有一塊 100mb 的連續空閒記憶體空間,你每次都會從中申請一塊 30mb 的記憶體。那麼當你申請了 3 次後,這塊記憶體就只剩下了 10mb 的空間,第 4 次申請的時候就會失敗。如果沒有其它的空間釋放並且每次申請的空間都比 10mb 大,那麼剩下的空間對於整塊記憶體來說就是記憶體碎片。

redis 中,最常用的是寫入、修改、刪除資料。這些操作在執行後都會產生 一定程度的記憶體碎片。

redis 中分配記憶體是根據固定的大小來劃分記憶體空間的。為了減少分配次數,redis 會根據申請的記憶體最接近的固定值分配相應大小的空間。

什麼意思呢,假如 redis 按照 8 位元組、16 位元組、32 位元組、48 位元組等來分配記憶體。當你想要儲存乙個 18 位元組的資料時,此時 redis 就會分配 32 位元組(因為 32 是與 18 最接近的固定值)。如果這時候,再寫入的資料需要的記憶體空間在 14 個位元組內,那 redis 就無需再進行分配了。

這就像你有不同的箱子,為了裝東西,你需要找乙個體積最接近的箱子來裝。但是裝進去後,你發現還有空間可以放一些小東西,就無需再找箱子了。

但是,這種分配空間的方式會帶來一定程度的記憶體碎片。我們可以把固定大小的劃分空間看成不同體積的箱子,每種箱子裡的空間不同程度上都會有剩餘。這些剩餘的空間就是記憶體碎片。

鍵值對進行修改時,可能會變大也會變小,相應的就會占用額外空間或者釋放不用的空間。

如圖中所示,當前 a、b、c 分別占用了 3、2、4 個位元組,將 a 從 3 位元組修改為 2 位元組時,此時就會有 1 個位元組的空間空了出來,這時就會出現 1 個位元組的碎片。

那如果我將資料 a 從 3 位元組修改為 4 位元組呢?此時為了保持資料 a 的空間連續性,作業系統會把 b 拷貝到別的空間。此時又會出現 1 個位元組的碎片。

理解了修改資料,刪除資料就很容易明白了。還是上邊的例子,此時刪除了資料 b,那麼就釋放了 2 個位元組的空間。這樣對於整個記憶體空間來說就產生了 2 個位元組的碎片。

你可能會有疑問,記憶體碎片會有什麼危害呢?

我們還是以上邊的箱子來表示。你想想,如果你要把這些箱子都裝上車運走,每個箱子裡都有空出來的空間(記憶體碎片),那麼執行一次的效率及價效比是不是會很低。同樣,在 redis 中,由於大量的碎片存在,會導致實際利用率變低。

那麼我們有沒有辦法來解決記憶體碎片呢?

第一種方式很簡單,直接推倒重來。也就是把 redis 直接重啟完事兒,記憶體一斷電全世界就清淨。但是這種暴力省事的方式卻有很多隱患。

生產環境中你這麼搞的話得提前燒燒香,保佑不會出什麼問題。如果你沒進行過持久化,那麼就別燒了,燒了也沒用。如果有持久化的話,那麼恢復時長還得取決你持久化檔案的大小,在這個階段還無法提供服務。糟心不?

那麼有沒有不這麼刺激的方式。

有的,高版本的 redis 提供了記憶體碎片清理的方式。一言以蔽之,就是空間置換。

怎麼個置換法?我們的目的是為了消除記憶體碎片,那麼我們把已使用的記憶體資料重新整理到一起不就行了嗎?讓不連續的空間變成連續的,剩下的空間,繼續來分配。

畫個圖理解下:

但是,說說還是挺容易的,理論到實踐中間還隔著效能損耗。

在進行多次資料拷貝過程中,單執行緒的 redis 只能幹等著,無法響應客戶端的請求。這時候只能乾瞪眼,效能太受影響。 

涼,那該咋整?!別急,有緩解的策略,你接著往下看。

redis 中有專門的引數設定用來進行自動清理記憶體碎片:activedefrag yes

這個命令是啟動清理功能的,這還不夠,redis 中還需要其他的條件限制才能夠進行清理。

下面引數都是滿足任一條件後就可以進行清理:

在處理的過程中,為了避免對正常請求的影響,同時又能保證效能。redis 同時還提供了監控 cpu 占用比例的引數,在滿足以下條件時才會保證清理正常開展:

檢視記憶體使用情況

記憶體碎片導致原因

如何解決記憶體碎片

敗家玩意兒!Redis 竟然浪費了這麼多記憶體!

作為記憶體資料庫,記憶體空間大小對於 redis 來說是至關重要的。記憶體越多,意味著儲存的資料也會越多。但是不知道你有沒有遇到過這樣的情況,明明空間很大,但是記憶體的使用卻不是很理想。為什麼會出現這樣的情況呢?這期我們就來看看這個 詭異 的事件。坐好了,準備發車!思維導圖 檢視記憶體使用情況 首先...

QT 完蛋玩意兒 motedialog cpp

include notedialog.h include ui notedialog.h include adddialog.h include ui adddialog.h include include include include include include include includ...

QT 完蛋玩意兒 addDialog cpp

include adddialog.h include ui adddialog.h adddialog adddialog qwidget parent qdialog parent m ui new ui adddialog adddialog adddialog void adddialog ...