如何避免內存溢出?—— Redis內存使用和管理知識總結
Redis是當今很火爆的內存數據庫,我們的所有數據都存在了內存之中,因此我們的每次寫入、讀取都是從內存中進行操作,所以在帶來速度的同時,也從內存的使用上給我們帶來了挑戰。眾所周知,在硬件資源中內存價格是高于硬盤價格的,通過學習Redis的內存知識可以使我們在保護Redis數據庫的同時更高效的發揮出Redis的作用,進而管理內存,減少內存消耗和硬件成本。
Redis作為內存數據庫,對于自身所使用的內存情況是有命令可以統計的,通過獲取到的相關信息可以了解Redis自身內存的使用現狀,進而有助于判斷內存使用健康度。Redis提供查看內存的指令為info memory。
在以上各項指標中需要重點關注的指標有:used_memory_rss和used_memory以及它們的比值mem_fragmentation_ratio。
當mem_fragmentation_ratio>1 時,說明used_memory_rss-used_memory多出的部分內存并沒有用于數據存儲,而是被內存碎片所消耗,這個值越大,表明內存碎片越多。
當mem_fragmentation_ratio<1 時,這種情況說明正在使用虛擬內存,也就是在使用主機的硬盤,由于硬盤性能是遠遠低于內存的,所以要小心因為性能問題導致整體Redis故障。根據日常使用的情況mem_fragmentation_ratio的數值在1 ~ 1.5之間是比較健康的。在出現內存碎片過多的問題怎么處理呢?最簡單暴力的辦法就是重啟,在Redis4.0版本之后支持在運行期進行自動內存碎片清理,主要通過設置config set activedefrag yes來進行實現,同時也提供了memory purge命令來手動進行內存碎片清理。
Redis默認是無限使用內存的,所以防止系統內存被耗盡,需要對Redis的內存上限進行設置,Redis使用maxmemory參數限制最大可用內存。通過前面的介紹我們可以得知maxmemory配置的是Redis實際使用的內存量,也就是used_memory統計項對應的內存。由于內存碎片率的存在,實際消耗的內存可能會比maxmemory設置的更大,實際使用時要小心這部分內存溢出。根據慣例一般會預留出20%的服務器空閑內存防止內存溢出通過。
Redis的內存上限可以通過config set maxmemory進行動態修改,即修改最大可用內存。通過動態修改maxmemory,可以實現在當前服務器下動態伸縮Redis內存的目的,考慮到現在在部署Redis時大多采用集群或哨兵模式,單臺主機上并非Redis單實例,因此建議針對所有的Redis進程都要配置maxmemory。
Redis針對內存使用情況提供內存回收策略供運維人員進行配置,主要用于刪除到達過期時間的鍵對象以及當Redis內存使用達到所設置的maxmemory上限時則執行內存回收策略。
Redis所有的鍵都可以設置過期屬性,在數據庫結構中的expires字典中保存了數據庫中所有鍵的過期時間,我們稱expire這個字典為過期字典。由于進程內保存大量的鍵,維護每個鍵精準的過期刪除機制會導致消耗大量的CPU,對于單線程的Redis來說成本過高,因此Redis采用惰性刪除和定時任務刪除機制實現過期鍵的內存回收。
惰性刪除:惰性刪除用于當客戶端讀取帶有超時屬性的鍵時,如果已經超過鍵設置的過期時間,會執行刪除操作并返回空,這種刪除策略對CPU是友好的,刪除操作只有在不得不的情況下才會進行,不會對其他的expire key上浪費無謂的CPU時間。但是這種策略對內存不友好,一個key已經過期,但是在它被操作之前不會被刪除,仍然占據內存空間。如果有大量的過期鍵存在但是又很少被訪問到,那會造成大量的內存空間浪費。因為可能存在一些key永遠不會被再次訪問到,這些設置了過期時間的key也是需要在過期后被刪除的,我們甚至可以將這種情況看作是一種內存泄露—-無用的垃圾數據占用了大量的內存,而服務器卻不會自己去釋放它們,這對于運行狀態非常依賴于內存的Redis服務器來說,肯定不是一個好消息。正因為如此,Redis還提供另一種定時任務刪除機制作為惰性刪除的補充。
定時任務刪除:Redis內部維護一個定時任務,默認每秒運行10次(通過配置server.hz控制)。Redis會周期性的隨機測試一批設置了過期時間的key并進行處理。測試到的已過期的key將被刪除。
當Redis所用內存達到maxmemory上限時會觸發相應的溢出控制策略。具體策略受maxmemory-policy參數控制,Redis支持6種策略,如下所示:
1)noeviction:默認策略,數據永不過期,不會刪除任何數據,當內存不足以容納新寫入數據時,新寫入操作會報錯,一般不推薦使用。
2)volatile-lru:根據LRU算法刪除設置了超時屬性(expire)的鍵,直到騰出足夠空間為止。如果沒有可刪除的鍵對象,回退到noeviction策略。這種情況一般是把 Redis 既當緩存,又做持久化存儲的時候才用。
3)allkeys-lru:根據LRU算法刪除鍵,不管數據有沒有設置超時屬性,直到騰出足夠空間為止。一般推薦使用該策略
4)allkeys-random:內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個 Key。
5)volatile-random:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個 Key。
6)volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期時間的 Key 優先移除。如果沒有,回退到noeviction策略。
內存溢出控制策略可以采用config set maxmemory-policy{policy}動態配置。我們上文已經介紹了Redis所支持的的內存溢出應對策略,運維人員可以根據實際需求靈活定制。
綜上所述,我們從Redis數據庫最關鍵特性—內存出發,詳細介紹了Redis的內存使用和內存管理,而這也是成為一名專業Redis運維人員的核心技能。大家通過本文能夠提升對Redis內存的認識,但要真正掌握則需要長時間的學習和使用,希望大家共同努力學習和進步。