首頁  >  文章  >  資料庫  >  詳細解析Redis記憶體滿了怎麼去優化

詳細解析Redis記憶體滿了怎麼去優化

WBOY
WBOY轉載
2022-03-14 17:09:273397瀏覽

這篇文章為大家帶來了關於Redis的相關知識,其中主要介紹了redis的記憶體滿了應該怎麼優化的相關問題,還包括了淘汰機制、LRU演算法以及處理淘汰的數據,希望對大家有幫助。

詳細解析Redis記憶體滿了怎麼去優化

推薦學習:Redis學習教學

Redis記憶體滿了怎麼辦?怎麼優化記憶體?

MySQL裡有2000w數據,redis中只存20w的數據,如何保證redis中的數據都是熱點數據

redis內存數據集大小上升到一定大小的時候,就會施行資料淘汰策略。

Redis主要消耗什麼實體資源?

記憶體。

Redis的記憶體用完了會發生什麼事?

如果達到設定的上限,Redis的寫指令會回傳錯誤訊息(但是讀取指令還可以正常回傳。)或者你可以設定記憶體淘汰機制,當Redis達到記憶體上限時會沖刷掉舊的內容。

談談快取資料的淘汰機制

Redis 快取有哪些淘汰策略?

  • 不進行資料淘汰的策略,只有 noeviction 這一種。

會進行淘汰的7 種策略,我們可以再進一步根據淘汰候選資料集的範圍把它們分成兩類:

  • 在設定了過期時間的數據中進行淘汰,包括volatile-random、volatile-ttl、volatile-lru、volatile-lfu四種。
  • 在所有資料範圍內進行淘汰,包括 allkeys-lru、allkeys-random、allkeys-lfu三種。
##volatile-lfu使用LFU 演算法選擇設定了過期時間的鍵值對
策略 規則
volatile-ttl 在篩選時,會針對設定了過期時間的鍵值對,根據過期時間的先後進行刪除,越早過期的越先被刪除。
volatile-random 在設定了過期時間的鍵值對中,進行隨機刪除。
volatile-lru 使用LRU 演算法篩選設定了過期時間的鍵值對
策略
#######allkeys-random######從所有鍵值對中隨機選擇並刪除資料;############allkeys -lru######使用LRU 演算法在所有資料中進行篩選############vallkeys-lfu######使用LFU 演算法在所有資料中進行篩選### ##########

談談LRU演算法

是按照最近最少使用的原則來篩選數據,最不常用的數據會被篩選出來,而最近頻繁使用的數據會留在快取中。

那具體是怎麼篩選的呢? LRU 會把所有的資料組織成一個鍊錶,鍊錶的頭和尾分別表示 MRU 端和 LRU 端,分別代表最近最常使用的資料和最近最不常用的資料。
詳細解析Redis記憶體滿了怎麼去優化
LRU 演算法背後的想法非常樸素:它認為剛剛被訪問的數據,肯定還會再次訪問,所以就把它放在MRU 端;長久不訪問的數據,肯定就不會再被存取了,所以就讓它逐漸後移到LRU 端,在快取滿時,就優先刪除它。

問題:LRU 演算法在實際實作時,需要用鍊錶管理所有的快取數據,這會帶來額外的空間開銷。而且,當有資料被存取時,需要在鍊錶上把該資料移動到 MRU 端,如果有大量資料被訪問,就會帶來很多鍊錶移動操作,會很耗時,進而降低 Redis 快取效能。

解決
在 Redis 中,LRU 演算法被做了簡化,以減輕資料淘汰對快取效能的影響。具體來說,Redis 預設會記錄每個資料的最近一次存取的時間戳記(由鍵值對資料結構 RedisObject 中的 lru 欄位記錄)。然後,Redis 在決定淘汰的數據時,第一次會隨機選出 N 個數據,把它們當作一個候選集合。接下來,Redis 會比較這 N 個資料的 lru 字段,把 lru 字段值最小的資料從快取中淘汰出去。
當需要再次淘汰資料時,Redis 需要挑選資料進入第一次淘汰時所建立的候選集合。這兒的挑選標準是:能進入候選集合的資料的 lru 欄位值必須小於候選集合中最小的 lru 值。當有新資料進入候選資料集後,如果候選資料集中的資料個數達到了 maxmemory-samples,Redis 就把候選資料集中 lru 欄位值最小的資料淘汰出去。

使用建議

  • 優先使用 allkeys-lru 策略。這樣,可以充分利用 LRU 這項經典快取演算法的優勢,把最近最常存取的資料留在快取中,提升應用程式的存取效能。如果你的業務資料中有明顯的冷熱資料區分,我建議你使用 allkeys-lru 策略。
  • 如果業務應用中的資料存取頻率相差不大,沒有明顯的冷熱資料區分,建議使用 allkeys-random 策略,隨機選擇淘汰的資料就行。
  • 如果你的業務中有置頂的需求,例如置頂新聞、置頂視頻,那麼,可以使用 volatile-lru 策略,同時不給這些置頂數據設定過期時間。這樣一來,這些需要置頂的資料一直不會被刪除,而其他資料會在過期時根據 LRU 規則進行篩選。

如何處理被淘汰的資料?

一旦被淘汰的數據選定後,如果這個數據是乾淨數據,那麼我們就直接刪除;如果這個數據是髒數據,我們需要把它寫回數據庫。

那要怎麼判斷一個資料到底是乾淨的還是髒的呢?

  • 乾淨資料和髒資料的差別在於,和最初從後端資料庫讀取時的值相比,有沒有被修改過。乾淨資料一直沒有被修改,所以後端資料庫裡的資料也是最新值。在替換時,它可以直接刪除。
  • 而髒資料就是曾經被修改過的,已經和後端資料庫中保存的資料不一致了。此時,如果不把髒資料寫回資料庫中,這個資料的最新值就遺失了,就會影響應用程式的正常使用。

即使淘汰的資料是髒數據,Redis 也不會把它們寫回資料庫。所以,我們在使用 Redis 快取時,如果資料被修改了,需要在資料修改時就將它寫回資料庫。否則,這個髒資料被淘汰時,會被 Redis 刪除,而資料庫裡也沒有最新的資料了。

Redis怎麼優化記憶體?

1、控制key的數量:當使用Redis儲存大量資料時,通常會存在大量鍵,過多的鍵同樣會消耗大量記憶體。 Redis本質是一個資料結構伺服器,它為我們提供多種資料結構,如hash,list,set,zset 等結構。使用Redis時不要進入一個誤區,大量使用get/set這樣的API,把Redis當成Memcached使用。對於儲存相同的資料內容利用Redis的資料結構降低外層鍵的數量,也可以節省大量記憶體。
2、縮減鍵值對象,降低Redis記憶體使用最直接的方式就是縮減鍵(key)和值(value)的長度。

  • key長度:如在設計鍵時,在完整描述業務情況下,鍵值越短越好。
  • value長度:值物件縮減比較複雜,常見需求是把業務物件序列化成二進位陣列放入Redis。首先應該在業務上精簡業務對象,去掉不必要的屬性避免儲存無效資料。其次在序列化工具選擇上,應該選擇更有效率的序列化工具來降低位元組數組大小。

3、編碼最佳化。 Redis對外提供了string,list,hash,set,zet等類型,但是Redis內部針對不同類型存在編碼的概念,所謂編碼就是具體使用哪種底層資料結構來實現。編碼不同將直接影響資料的記憶體佔用和讀寫效率。


  • 1、redisObject物件
    詳細解析Redis記憶體滿了怎麼去優化

#type欄位
利用集合類型數據,因為通常情況下很多小的Key-Value可以用更緊湊的方式存放在一起。盡可能使用散列表(hashes),散列表(是說散列表裡面儲存的數少)使用的記憶體非常小,所以你應該盡可能的將你的資料模型抽象化到一個散列表裡面。例如你的web系統中有一個用戶對象,不要為這個用戶的名稱,姓氏,郵箱,密碼設定單獨的key,而是應該把這個用戶的所有資訊儲存到一張散列表裡面。

encoding欄位:
採用不同的編碼實作記憶體佔用有明顯差異

lru欄位:
開發提示:可以使用scan object idletime 命令批量查詢哪些鍵長時間未被訪問,找出長時間不訪問的鍵進行清理降低內存佔用。

refcount欄位:
當物件為整數且範圍在[0-9999]時,Redis可以使用共享物件的方式來節省記憶體。

ptr欄位 :
開發提示:高並發寫入場景中,在條件允許的情況下建議字串長度控制在39位元組以內,減少創建redisObject記憶體分配次數從而提高效能。


  • 2、縮減鍵值物件
    降低Redis記憶體使用最直接的方式就是縮減鍵(key)和值(value)的長度。
    可以使用通用壓縮演算法壓縮json,xml後再存入Redis,從而降低記憶體佔用

  • 3、共享物件池
    物件共享池指Redis內部維護[0-9999]的整數物件池。創建大量的整數類型redisObject存在記憶體開銷,每個redisObject內部結構至少佔16字節,甚至超過了整數自身空間消耗。所以Redis記憶體維護一個[0-9999]的整數物件池,用於節約記憶體。除了整數值對象,其他型別如list,hash,set,zset內部元素也可以使用整數物件池。 因此開發中在滿足需求的前提下,盡量使用整數物件以節省記憶體。
    當設定maxmemory並啟用LRU相關淘汰策略如:volatile-lru,allkeys-lru時,Redis禁止使用共享物件池。

為什麼開啟maxmemory和LRU淘汰策略後物件池無效?
LRU演算法需要取得物件最後被存取時間,以便淘汰最長未存取數據,每個物件最後存取時間儲存在redisObject物件的lru欄位。物件共享意味著多個引用共享同一個redisObject,這時lru欄位也會被共享,導致無法取得每個物件的最後存取時間。如果沒有設定maxmemory,直到記憶體被用盡Redis也不會觸發記憶體回收,所以共享物件池可以正常運作。
綜上所述,共享物件池與maxmemory LRU策略衝突,使用時需要注意。

為什麼只有整數物件池?
首先整數物件池復用的幾率最大,其次物件共享的一個關鍵操作就是判斷相等性,Redis之所以只有整數物件池,是因為整數比較演算法時間複雜度為O(1),只保留一萬個整數為了防止物件池浪費。如果是字串判斷相等性,時間複雜度變成O(n),特別是長字串更消耗效能(浮點數在Redis內部使用字串儲存)。對於較複雜的資料結構如hash,list等,相等性判斷需要O(n2)。對於單執行緒的Redis來說,這樣的開銷顯然不合理,因此Redis只保留整數共享物件池。


  • 4、字串最佳化
    Redis沒有採用原生C語言的字串型別而是自己實作了字串結構,內部簡單動態字串,簡稱SDS。

字串結構

  • 特點:
    O(1)時間複雜度取得:字串長度,已用長度,未用長度。
    可用於保存位元組數組,支援安全的二進位資料儲存。
    內部實作空間預先分配機制,降低記憶體再分配次數。
    惰性刪除機制,字串縮減後的空間不釋放,作為預先分配空間保留。

預先分配機制

  • 開發提示:盡量減少字串頻繁修改操作如append,setrange, 改為直接使用set修改字串,降低預分配帶來的記憶體浪費和記憶體碎片化。

字串重構:基於hash類型的次要編碼方式。

  • 二級編碼怎麼用?
    二級編碼方法中所採用的 ID 長度是有講究的。
    涉及一個問題–Hash 類型底層結構小於設定值時使用壓縮列表,大於設定值時使用哈希表。
    一旦從壓縮列表轉為了哈希表,Hash 類型會一直用哈希表進行保存,而不會再轉回壓縮列表。
    在節省記憶體空間方面,哈希表就沒有壓縮列表那麼有效率。為能充分使用壓縮清單的精簡記憶體佈局,一般要控制保存在 Hash 中的元素個數。

  • 5.編碼優化
    使用壓縮列表ziplist編碼的hash類型依然比使用hashtable編碼的集合節省大量記憶體。

  • 6.控制key的數量
    開發提示:使用ziplist hash優化keys後,如果想使用逾時刪除功能,開發人員可以儲存每個物件寫入的時間,再透過定時任務使用hscan指令掃描數據,找出hash內逾時的資料項刪除即可。

當Redis記憶體不足時,首先考慮的問題不是加機器做水平擴展,應該先嘗試做記憶體優化。當遇到瓶頸時,再去考慮水平擴展。即使對於叢集化方案,垂直層面最佳化也同樣重要,避免不必要的資源浪費和叢集化後的管理成本。

推薦學習:Redis教學

#

以上是詳細解析Redis記憶體滿了怎麼去優化的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除