理解Redis的內存
簡(jiǎn)介
Redis 是一種內存數據庫,將數據保存在內存中,讀寫(xiě)效率要比傳統的將數據保存在磁盤(pán)上的數據庫要快很多。所以,監控 Redis 的內存消耗并了解 Redis 內存模型對高效并長(cháng)期穩定使用 Redis 至關(guān)重要。
Redis 內存統計
redis提供了下面命令來(lái)查詢(xún)當前redis內存的使用情況。
info memory
執行結果如下:
具體指標如下所示:
屬性 | 說(shuō)明 |
---|---|
used_memory | Redis 分配器分配的內存總量,也就是內部存儲的所有數據內存占用量 |
used_memory_human | 以可讀的格式返回 used_memory |
used_memory_rss | 從操作系統的角度顯示 Redis 進(jìn)程占用的物理內存總量 |
used_memory_rss_human | used_memory_rss 的用戶(hù)宜讀格式的顯示 |
used_memory_peak | 內存使用的最大值,表示 used_memory 的峰值 |
used_memory_peak_human | 以可讀的格式返回 used_memory_peak的值 |
used_memory_lua | Lua 引擎所消耗的內存大小。 |
mem_fragmentation_ratio | used_memory_rss / used_memory 的比值,可以代表內存碎片率 |
maxmemory | Redis 能夠使用的最大內存上限,0表示沒(méi)有限制,以字節為單位。 |
maxmemory_policy | Redis 使用的內存回收策略,可以是 noeviction、allkeys-lru、volatile-lru、allkeys-random、volatile-random 或者 volatile-ttl。默認是noeviction,也就是不會(huì )回收。 |
mem_fragmentation_ratio一般大于1,且該值越大,內存碎片比例越大,mem_fragmentation_ratio<1,說(shuō)明Redis使用了虛擬內存,由于虛擬內存的媒介是磁盤(pán),比內存速度要慢很多,當這種情況出現時(shí),應該及時(shí)排查,如果內存不足應該及時(shí)處理,如增加Redis節點(diǎn)、增加Redis服務(wù)器的內存、優(yōu)化應用等。
一般來(lái)說(shuō),mem_fragmentation_ratio在1.03左右是比較健康的狀態(tài)(對于jemalloc來(lái)說(shuō))
Redis內存劃分
Redis作為內存數據庫,在內存中存儲的內容主要是數據(鍵值對);通過(guò)前面的敘述可以知道,除了數據以外,Redis的其他部分也會(huì )占用內存。
Redis的內存占用主要可以劃分為以下幾個(gè)部分:
對象內存
為數據庫,數據是最主要的部分;這部分占用的內存會(huì )統計在used_memory中。
Redis使用鍵值對存儲數據,其中的值(對象)包括5種類(lèi)型,即字符串、哈希、列表、集合、有序集合。這5種類(lèi)型是Redis對外提供的,實(shí)際上,在Redis內部,每種類(lèi)型可能有2種或更多的內部編碼實(shí)現;此外,Redis在存儲對象時(shí),并不是直接將數據扔進(jìn)內存,而是會(huì )對對象進(jìn)行各種包裝:如redisObject、SDS等;這篇文章后面將重點(diǎn)介紹Redis中數據存儲的細節。
進(jìn)程本身運行內存
Redis主進(jìn)程本身運行肯定需要占用內存,如代碼、常量池等等;這部分內存大約幾兆,在大多數生產(chǎn)環(huán)境中與Redis數據占用的內存相比可以忽略。這部分內存不是由jemalloc分配,因此不會(huì )統計在used_memory中。
補充說(shuō)明:除了主進(jìn)程外,Redis創(chuàng )建的子進(jìn)程運行也會(huì )占用內存,如Redis執行AOF、RDB重寫(xiě)時(shí)創(chuàng )建的子進(jìn)程。當然,這部分內存不屬于Redis進(jìn)程,也不會(huì )統計在used_memory和used_memory_rss中。
緩沖內存
緩沖內存包括客戶(hù)端緩沖區、復制積壓緩沖區、AOF緩沖區等;其中,客戶(hù)端緩沖存儲客戶(hù)端連接的輸入輸出緩沖;復制積壓緩沖用于部分復制功能;AOF緩沖區用于在進(jìn)行AOF重寫(xiě)時(shí),保存最近的寫(xiě)入命令。在了解相應功能之前,不需要知道這些緩沖的細節;這部分內存由jemalloc分配,因此會(huì )統計在used_memory中。
輸入緩沖無(wú)法控制,最大空間為 1G,如果超過(guò)將斷開(kāi)連接。而且輸入緩沖區不受 maxmemory 控制,假設一個(gè) Redis 實(shí)例設置了 maxmemory 為 4G,已經(jīng)存儲了 2G 數據,但是如果此時(shí)輸入緩沖區使用了 3G,就已經(jīng)超出了 maxmemory 限制,可能導致數據丟失、鍵值淘汰或者 OOM。
內存碎片
內存碎片是Redis在分配、回收物理內存過(guò)程中產(chǎn)生的。例如,如果對數據的更改頻繁,而且數據之間的大小相差很大,可能導致redis釋放的空間在物理內存中并沒(méi)有釋放,但redis又無(wú)法有效利用,這就形成了內存碎片。內存碎片不會(huì )統計在used_memory中。
內存碎片的產(chǎn)生與對數據進(jìn)行的操作、數據的特點(diǎn)等都有關(guān);此外,與使用的內存分配器也有關(guān)系:如果內存分配器設計合理,可以盡可能的減少內存碎片的產(chǎn)生。后面將要說(shuō)到的jemalloc便在控制內存碎片方面做的很好。
如果Redis服務(wù)器中的內存碎片已經(jīng)很大,可以通過(guò)安全重啟的方式減小內存碎片:因為重啟之后,Redis重新從備份文件中讀取數據,在內存中進(jìn)行重排,為每個(gè)數據重新選擇合適的內存單元,減小內存碎片。
內存回收策略
Redis的內存回收機制主要體現在兩個(gè)方面上:
對過(guò)期數據的處理 當內存使用情況達到maxmemory時(shí)觸發(fā)內存回收策略
過(guò)期鍵的刪除
惰性刪除:什么時(shí)候執行呢?就是在客戶(hù)端讀取帶有超時(shí)屬性的鍵時(shí),如果已經(jīng)超過(guò)鍵值設置的過(guò)期時(shí)間,則刪除并返回空。這樣做的目的主要是為了節省CPU成本考慮,不需要單獨維護TTL鏈表來(lái)處理過(guò)期鍵的刪除。但是,如果單獨使用這種方式存在一個(gè)問(wèn)題,如果當前的鍵值永遠不再被訪(fǎng)問(wèn)呢?就不刪除了嗎?那肯定不行,這就會(huì )造成內存泄漏的問(wèn)題。那Redis是怎么解決的呢?Redis提供了一個(gè)定時(shí)任務(wù)的刪除機制來(lái)做補充。
定時(shí)任務(wù)刪除
Redis內部維護了一個(gè)定時(shí)任務(wù),默認是每秒運行十次。刪除的邏輯如下圖:
內存溢出控制策略
當Redis使用的內存達到上限maxmemory后,就會(huì )根據maxmemory-policy設置的相關(guān)策略進(jìn)行對應的操作,Redis支持一下6種策略:
策略 | 備注 |
---|---|
noeviction | 默認策略,不會(huì )刪除任何數據,拒絕所有寫(xiě)入操作并返回客戶(hù)端錯誤信息(error)OOM command not allowed when used memory |
volatile-lru | 只對設置有超時(shí)屬性的Key根據LRU算法執行刪除操作,如果沒(méi)有可刪除的Key,則回退到noeviction策略 |
allkeys-lru | 針對所有的key,根據LRU算法執行刪除操作直到回收到足夠內存空間 |
allkeys-random | 隨機刪除所有的鍵,直到騰出足夠空間 |
volatile-random | 針對帶有過(guò)期屬性的鍵,進(jìn)行刪除操作,直到騰出足夠空間 |
volatile-ttl | 根據鍵值對象的ttl屬性,刪除最近將要過(guò)期數據。如果沒(méi)有,則回退到noviction策略 |
參考:
https://juejin.im/entry/5b93ce4d5188255c48349316
