used_memory:已經使用了的記憶體大小,包括redis程序內部開銷和你的cache的資料所占用的記憶體,單位byte。
used_memory_human:以可讀格式返回的used_memory。
used_memory_rss:從作業系統角度顯示的redis占用的物理記憶體。
used_memory_rss_human:以可讀格式返回的used_memory。
used_memory_peak:記憶體的最大使用值,表示used_memory的峰值。
used_memory_peak_human:
used_memory_memory_lua: lua引擎所消耗的記憶體大小
mem_fragmentation_ratio: used_memory_rss/used_memory的比值,表示記憶體碎片率
used_allocator:redis所使用的記憶體分配器
其中,我們需要重點關注的指標有:used_memory_rss和used_memory以及它們的比值mem_fragmentation_ratio
特別是mem_fragmentation_ratio:
當mem_fragmentation_ratio > 1時,說明有很多記憶體碎片,即被使用了但是沒有實際用於儲存資料的記憶體。對於乙個存有大量資料的redis伺服器,該值起碼需要在1.5以下才算正常,否則需要思考一下如何做記憶體優化。
當mem_fragmentation_ratio < 1時,說明redis使用了虛擬記憶體,即redis把記憶體的資料交換到了硬碟上,這種情況要格外關注。因為硬碟的讀寫速度要遠遠慢於記憶體,如果請求的資料大量儲存在硬碟上,redis的效能會變得很差,甚至僵死。
如圖:
1.自身記憶體:redis自身執行所消耗的記憶體,一般很小。
2.物件記憶體:這是redis消耗記憶體最大的一塊,儲存著使用者所有的資料,每次存入key-value的鍵值對,暫時可以簡單的理解為sizeof(key+value)的記憶體大小消耗。
3.緩衝記憶體:緩衝記憶體主要包括:客戶端緩衝、複製積壓緩衝區、aof緩衝區。
客戶端緩衝:是指客戶端連線redis之後,輸入或者輸出資料的緩衝區,其中輸出緩衝可以通過配置引數引數client-output-buffer-limit控制。
複製積壓緩衝區:乙個可重用的固定大小緩衝區用於實現部分複製功能,根據repl-backlog-size引數控制,預設1mb。對
於複製積壓緩衝區整個主節點只有乙個,所有的從節點共享此緩衝區,因此可以設定較大的緩衝區空間,如100mb,這部分記憶體投入是有價值的,可以有效避免全量複製。
aof緩衝區:這部分空間用於在redis重寫期間儲存最近的寫入命令,aof緩衝區空間消耗使用者無法控制,消耗的記憶體取決於aof重寫時間和寫入命令量,這部分空間占用通常很小。
4.記憶體碎片:redis的記憶體分配器為了更好地管理和重複利用記憶體,分配記憶體策略一般採用固定範圍的記憶體塊進行分配,如8byte,16byte……4kb,8kb……4mb…….每乙個資料塊的大小是固定的,如果當前輸入的key-value實際占用的大小是6kb,那麼redis可能申請乙個8kb的記憶體塊來儲存這乙份資料,那麼該8kb記憶體塊對於伺服器來講已經分配出去,不會再讓其他的資料占用,但是實際上此次存入資料的時候,有2kb的記憶體是沒有使用到的,所以這個插入操作產生了2kb的記憶體碎片。當然,這是所有記憶體分配器無法避免的通病,但是可以優化。
由上面的介紹可以看到,如果不對redis的記憶體進行優化,那麼對乙個64g記憶體的redis伺服器,如果mem_fragmentation_ratio的值過大(即記憶體碎片太多),產生的結果就是實際上只儲存了30g不到的資料,記憶體就已經達到了瓶頸(一般redis配置的最大使用記憶體需要比伺服器實際記憶體小一些,留給伺服器一些餘地),這樣就極大的浪費了珍貴的記憶體資源,顯然是不合理的,所以如果要優化redis的記憶體,首先得對記憶體碎片進行優化。
優化記憶體碎片:
1.使用更好的記憶體分配器:
由上面的info memory命令可以看到,used_allocator引數的值是libc,表示當前redis使用的記憶體分配器是glibc,另乙個redis的記憶體分配器jemalloc。jemalloc針對碎片化問題專門做了優化,一般不會存在過度碎片化的問題,使用jemalloc正常的碎片率(mem_fragmentation_ratio)在1.03左右。
3.大量的過期鍵:
由於redis對過期鍵惰性刪除的,比如我在redis中設定了乙個name-lihua的key-value鍵值對,然後將它的過期時間設定為了1天,但是當該鍵過期之後,程式並沒有再訪問name鍵。這種情況下即便該鍵過期了,redis卻並沒有在記憶體中將這個key-value刪除,只有在客戶端再次訪問到這個超時的鍵name時,redis才會執行刪除操作並返回空。這種策略是出於節省cpu成本考慮,不需要單獨維護ttl鍊錶來處理過期鍵的刪除。
雖然redis內還維護了乙個定時任務,預設每10s執行一次,通過自適應的演算法來主動根據鍵的過期比例、使用快慢兩種速率模式**鍵,但是大量的過期鍵還是會造成釋放的空間無法得到充分利用。
4.資料對齊:
在當前業務條件允許的情況下,使用數字型別或者固定長度的字串,主動對齊redis的記憶體塊,也可以適當的避免產生記憶體碎片。
5.重啟redis服務:
對於沒有高可用的redis伺服器絕對不要這麼做,對於做過主從高可用的redis集群,適當的重啟乙個redis服務,也可以有效的改善記憶體使用。
減少redis使用的記憶體:
1.使用共享物件池:
在redis內部,維護了乙個0-9999的整型共享物件池。當執行如下兩行redis插入命令時:
set a 100
set b 100
鍵a和b所指向的記憶體是同一片記憶體,即整型100的記憶體位址,這樣有效的節省了儲存大量整型值所消耗的記憶體。所以,比如在一些業務上,需要判斷當前的某種操作型別,可以使用1,2,3,4來儲存,然後在程式邏輯中做具體的判斷。但是如果業務中有許多個類似的可以使用整型代替字串的場景,維護乙個使用整型共享物件處的業務消耗的記憶體是會大大減少的。需要注意的是整型共享物件池對list,hash,set,zset內部的整型元素也是適用的。
使用object refcount key 可以檢視當前的整型物件的引用個數,依此判斷該資料是否使用了共享物件池。因為在特定的lru淘汰策略下,共享記憶體池是無法使用的,詳情有興趣可以自行了解。
2.盡可能讓hash表以ziplist編碼儲存:
redis內部擁有許多種編碼方式,ziplist為壓縮編碼格式,如何配置redis,讓它能把各種**和資料格式以ziplist儲存這裡就不詳談,展開講篇幅太長,有興趣可以自己研究一下。但是ziplist壓縮的hash表和set表等查詢資料的時間複雜度會增加,最壞的查詢時間複雜度可能會達到o(n^2),所以使用ziplist編碼儲存雖然消耗的記憶體很少,但是對於資料量龐大的表並不推薦使用ziplist編碼來儲存。
3.控制鍵的數量:
上面提到了hash表以ziplist儲存能大量的節約記憶體,然而現實中很多人使用redis儲存資料時,大量使用get/set這樣的api。導致redis中存在著大量的string型別的key-value,如此多的key-value一樣會消耗大量的記憶體,因為string型別是無法以ziplist壓縮編碼來儲存的。所以,合理的設定好資料結構,降低外層鍵的數量,也可以大量節約記憶體。
如圖:
該業務中,在程式層將redis的所有key分類成為了三個業務key相關hash表和乙個閒散key的hash表,在redis儲存這些資料時,雖然同樣儲存了一樣數量的key-value,hash表壓縮之後使用的記憶體要遠遠小於直接在redis儲存字串型的key-value,大大提公升了記憶體的使用率。
4.縮短key-value的長度
盡量的使用最短的key和value來完成業務邏輯,也可以適當的減少redis所使用的記憶體。
Redis記憶體管理和優化02
好好整理了一下關於reids記憶體優化的知識,總算對redis記憶體管理有了乙個初步的認識。一.記憶體消耗分析 redis程序內消耗主要包括 自身記憶體 物件記憶體 緩衝記憶體 記憶體碎片,其中redis空程序自身記憶體消耗非常少,通常used memory rss在3mb左右,used memor...
Redis 記憶體優化
非常感謝 redis內部有很多的資料型別,這些在官方文件上都可以看到,下面是其內部優化的一些細節點 1.string 和 數字,在redis中如果儲存的是 123 redis是能夠識別出來這是乙個數字並且按照數字來儲存,節省儲存空間,當然除了這個優化之外,redis內部會構建乙個數字池,預設是100...
redis記憶體優化
在資料量小或者只有int資料的時候,redis採用更緊湊的資料結構來儲存資料 zset hash ziplist set int intset 這些特殊的資料結構是一整塊記憶體,避免來記憶體分片,節省了記憶體,並且增加了cache命中,所以雖然演算法複雜度變成了on,但其實更快了 對於只需要flag...