Redis4.0記憶體容量評估
文章目錄
Redis容量評估
計算Redis容量,並不只是僅僅計算key佔多少位元組,value佔多少位元組,因為Redis為了維護自身的資料結構,也會佔用部分記憶體,本文章簡單介紹每種資料型別(String
、Hash
、Set
、ZSet
、List
)佔用記憶體量,供做Redis容量評估時使用。
Redis記憶體模型
要評估Redis佔用記憶體量,首先需要了解Redis的記憶體模型。通過Redis的記憶體模型,才能不僅知其然,而且知其所以然。
檢視記憶體佔用
首先在redis-cli
命令列中鍵入memory stats
命令檢視下Redis記憶體佔用,大概如下圖所示:
1) "peak.allocated" #Redis啟動到現在,佔用記憶體的峰值
2) (integer) 850160
3) "total.allocated" #當前使用的記憶體總量,Redis分配器分配的記憶體總量(單位是位元組),包括使用的虛擬記憶體(即swap)
4) (integer) 827608
5) "startup.allocated" #Redis啟動完成使用的記憶體位元組數
6) (integer) 765616
7) "replication.backlog" #主從複製backlog使用的記憶體,預設10MB,backlog只在主從斷線重連時發揮作用,主從複製本身並不依賴此項。
8) (integer) 0
9) "clients.slaves" #主從複製中所有slave的讀寫緩衝區
10) (integer) 0
11) "clients.normal" #除slave外所有其他客戶端的讀寫緩衝區
12) (integer) 49630
13) "aof.buffer" #此項為aof持久化使用的快取和aofrewrite時產生的快取之和,如果關閉了appendonly那這項就一直為0
14) (integer) 0
15) "db.0" #redis每個db的元資訊使用的記憶體,這裡只使用了db0,所以只打印了db0的記憶體使用狀態
16) 1) "overhead.hashtable.main"
2) (integer) 72
3) "overhead.hashtable.expires"
4) (integer) 0
17) "overhead.total" #redis額外的總開銷記憶體位元組數; 即分配器分配的總記憶體total.allocated,減去資料實際儲存使用記憶體:startup.allocated+replication.backlog+clients.slaves+clients.normal+aof.buffer+dbx
18) (integer) 815318
19) "keys.count" #redis當前儲存的key總量
20) (integer) 1
21) "keys.bytes-per-key" #平均每個key的記憶體大小:(total.allocated - startup.allocated) / keys.count
22) (integer) 61992
23) "dataset.bytes" #所有資料所使用的記憶體:total.allocated - overhead.total
24) (integer) 12290
25) "dataset.percentage" #所有資料佔比:100 * dataset.bytes / (total.allocated - startup.allocated)
26) "19.5934348106384277"
27) "peak.percentage" #當前使用記憶體與歷史最高值比例
28) "97.6251003742218018"
29) "fragmentation" #記憶體碎片比率
30) "2.1039986610412598"
總上圖我們可以看出,Redis佔用的記憶體並不僅僅包括資料記憶體dataset.bytes
,還包括啟動初始化記憶體、主從複製佔用記憶體、緩衝區記憶體等。
記憶體劃分
Redis記憶體佔用主要可以劃分為如下幾個部分:
- 資料
Redis資料佔用記憶體dataset.bytes
包括key-value佔用記憶體、dicEntry
佔用記憶體、SDS
佔用記憶體等。
- 初始化記憶體
redis啟動初始化時使用的記憶體startup.allocated
,屬於額外記憶體overhead.total
的一部分。 - 主從複製記憶體
用於主從複製,屬於額外記憶體一部分。 - 緩衝區記憶體
緩衝記憶體包括客戶端緩衝區、複製積壓緩衝區、AOF緩衝區等;其中,客戶端緩衝儲存客戶端連線的輸入輸出緩衝;複製積壓緩衝用於部分複製功能;AOF緩衝區用於在進行AOF重寫時,儲存最近的寫入命令。在瞭解相應功能之前,不需要知道這些緩衝的細節;這部分記憶體由jemalloc分配,因此會統計在used_memory中。 - 記憶體碎片
記憶體碎片是Redis在分配、回收物理記憶體過程中產生的。例如,如果對資料的更改頻繁,而且資料之間的大小相差很大,可能導致redis釋放的空間在實體記憶體中並沒有釋放,但redis又無法有效利用,這就形成了記憶體碎片。
記憶體碎片涉及到記憶體碎片率fragmentation
,該值對於檢視記憶體是否夠用比較重要:
該值一般>1,數值越大,說明記憶體碎片越多。如果<1,說明Redis佔用了虛擬記憶體,而虛擬記憶體是基於磁碟的,速度會變慢,所以如果<1,就需要特別注意是否是記憶體不足了。
一般來說,mem_fragmentation_ratio在1.03左右是比較健康的狀態(對於jemalloc來說);上面截圖中的mem_fragmentation_ratio值很大,是因為還沒有向Redis中存入資料,Redis程序本身執行的記憶體使得used_memory_rss 比used_memory大得多。
Redis資料記憶體
在瞭解了Redis記憶體模型之後,我們可以知道,Redis所佔記憶體不僅僅時key-value,很包括其他記憶體佔用。現在我們就來了解下Redis資料記憶體是如何佔用的。
Redis資料記憶體分配
Redis資料記憶體除了包括key-value
,還包括dicEntry
、redisObject
、SDS
等。
- dicEntry:Redis是Key-Value資料庫,因此對每個鍵值對都會有一個dictEntry,裡面儲存了指向Key和Value的指標;next指向下一個dictEntry,與本Key-Value無關。
- key:key的值並不是直接以字串儲存,而是儲存在SDS結構中。
- redisObject:value的值既不是直接以字串儲存,也不是像Key一樣直接儲存在SDS中,而是儲存在redisObject中。
- SDS:Redis沒有直接使用C字串(即以空字元’\0’結尾的字元陣列)作為預設的字串表示,而是使用了SDS。SDS是簡單動態字串(Simple Dynamic String)的縮寫。
Redis資料記憶體計算
String
公式:
公式:
jemalloc在分配記憶體塊時會分配大於實際值的2n的值,例如實際值時6位元組,那麼會分配8位元組
資料型別 | 佔用量 |
---|---|
dicEntry | 24位元組,jemalloc會分配32位元組的記憶體塊 |
redisObject | 16位元組 |
key_SDS | key的長度+9,jemalloc分配>=該值的2n的值 |
val_SDS | value的長度+9,jemalloc分配>=該值的2n的值 |
key的個數 | 所有的key的個數 |
bucket個數 | 大於key的個數的2n次方,例如key個數是2000,那麼bucket=2048 |
指標大小 | 8byte |
例如執行set user.name=admin
,需要記憶體計算如下所示:
Hash
Hash結構在使用HTable資料型別時,value並不是指向一個SDS,而實又一個Dict結構。Dict結構儲存了Hash物件的鍵值對。
一個hmset命令最終會產生以下幾個消耗記憶體的結構:
- 1個dictEntry結構,24位元組,負責儲存當前的雜湊物件;
- 1個SDS結構,(key長度 + 9)位元組,用作key字串;
- 1個redisObject結構,16位元組,指向當前key下屬的dict結構;
- 1個dict結構,88位元組,負責儲存雜湊物件的鍵值對;
- n個dictEntry結構,24×n位元組,負責儲存具體的field和value,n等於field個數;
- n個redisObject結構,16×n位元組,用作field物件;
- n個redisObject結構,16×n位元組,用作value物件;
- n個SDS結構,(field長度 + 9)× n位元組,用作field字串;
- n個SDS結構,(value長度 + 9)× n位元組,用作value字串;
公式:
SortedSet
一個zadd命令最終會產生以下幾個消耗記憶體的結構:
- 1個dictEntry結構,24位元組,負責儲存當前的有序集合物件;
- 1個SDS結構,(key長度 + 9)位元組,用作key字串;
- 1個redisObject結構,16位元組,指向當前key下屬的zset結構;
- 1個zset結構,16位元組,負責儲存下屬的dict和zskiplist結構;
- 1個dict結構,88位元組,負責儲存集