redis2.2版本及以後,儲存集合資料的時候會採用記憶體壓縮技術,以使用更少的記憶體儲存更多的資料。如hashes,lists,sets和sorted sets,當這些集合中的所有數都小於乙個給定的元素,並且集合中元素數量小於某個值時,儲存的資料會被以一種非常節省記憶體的方式進行編碼,使用這種編碼理論上至少會節省10倍以上記憶體(平均節省5倍以上記憶體)。並且這種編碼技術對使用者和redis api透明。因為使用這種編碼是用cpu換記憶體,所以我們提供了更改閾值的方法,只需在redis.conf裡面進行修改即可.
hash-max-zipmap-entries 64 (2.6以上使用hash-max-ziplist-entries)
hash-max-zipmap-value 512 (2.6以上使用hash-max-ziplist-value)
list-max-ziplist-entries 512
list-max-ziplist-value 64
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
set-max-intset-entries 512
(集合中)如果某個值超過了配置檔案中設定的最大值,redis將自動把把它(集合)轉換為正常的雜湊表。這種操作對於比較小的數值是非常快的,但是,如果你為了使用這種編碼技術而把配置進行了更改,你最好做一下基準測試(和正常的不採用編碼做一下對比).
使用32位的redis,對於每乙個key,將使用更少的記憶體,因為32位程式,指標占用的位元組數更少。但是32的redis整個例項使用的記憶體將被限制在4g以下。使用make 32bit命令編譯生成32位的redis。rdb和aof檔案是不區分32位和64位的(包括位元組順序),所以你可以使用64位的reidis恢復32位的rdb備份檔案,相反亦然.
redis 2.2引入了位級別和字級別的操作: getrange, setrange, getbit 和 setbit.使用這些命令,你可以把redis的字串當做乙個隨機讀取的(位元組)陣列。例如你有乙個應用,用來標誌使用者的id是連續的整數,你可以使用乙個位圖標記使用者的性別,使用1表示男性,0表示女性,或者其他的方式。這樣的話,1億個使用者將僅使用12 m的記憶體。你可以使用同樣的方法,使用 getrange 和 setrange 命令為每個使用者儲存乙個位元組的資訊。這僅是乙個例子,實際上你可以使用這些原始資料型別解決更多問題。
小雜湊表(是說雜湊表裡面儲存的數少)使用的記憶體非常小,所以你應該盡可能的將你的資料模型抽象到乙個雜湊表裡面。比如你的web系統中有乙個使用者物件,不要為這個使用者的名稱,姓氏,郵箱,密碼設定單獨的key,而是應該把這個使用者的所有資訊儲存到一張雜湊表裡面.
我知道這部分的標題很嚇人,但是我將詳細解釋這部分內容.
一般而言,把乙個模型(model)表示為key-value的形式儲存在redis中非常容易,當然value必須為字串,這樣儲存不僅比一般的key value儲存高效,並且比memcached儲存還高效.
讓我們做個對比:一些key儲存了乙個物件的多個欄位要比乙個雜湊表儲存物件的多個字段占用更多的記憶體。這怎麼可能?從原理上講,為了保證查詢乙個資料總是在乙個常量時間內(o(1)),需要乙個常量時間複雜度的資料結構,比如說雜湊表.
但是,通常情況下,雜湊表只包括極少的幾個字段。當雜湊表非常小的時候,我們採用將資料encode為乙個o(n)的資料結構,你可以認為這是乙個帶有長度屬性的線性陣列。只有當n是比較小的時候,才會採用這種encode,這樣使用hget和hset命令的複雜度仍然是o(1):當雜湊表包含的元素增長太多的時候,雜湊表將被轉換為正常的雜湊表(極限值可以在redis.conf進行配置).
無論是從時間複雜度還是從常量時間的角度來看,採用這種encode理論上都不會有多大效能提公升,但是,乙個線性陣列通常會被cpu的快取更好的命中(線性陣列有更好的區域性性),從而提公升了訪問的速度.
既然雜湊表的字段及其對應的值並不是用redis objects表示,所以雜湊表的字段不能像普通的key一樣設定過期時間。但是這毫不影響對雜湊表的使用,因為雜湊表本來就是這樣設計的(我們相信簡潔比多功能更重要,所以嵌入物件是不允許的,雜湊表字段設定單獨的過期時間是不允許的).
所以雜湊錶能高效利用記憶體。這非常有用,當你使用乙個雜湊表儲存乙個物件或者抽象其他一類相關的字段為乙個模型時。但是,如果我們有乙個普通的key value業務需求怎麼辦?
假如我們想使用redis儲存許多小物件,這些物件可以使用json字串表示,也可能是html片段和簡單的key->boolean鍵值對。概況的說,一切皆字串,都可以使用string:string的形式表示.
我們假設要快取的物件使用數字字尾進行編碼,如:
我們可以這樣做。每次set的時候,把key分為兩部分,第一部分當做乙個key,第二部當做雜湊表字段。比如「object:1234」,分成兩部分:
我們使用除最後2個數字的部分作為key,最後2個數字做為雜湊表的字段。使用命令:
hset object:12 34 somevalue
如你所見,每個雜湊表將(理論上)包含100個字段,這是cpu資源和記憶體資源之間的乙個折中.
另乙個需要你關注的是在這種模式下,無論快取多少物件,每個雜湊表都會分配100個字段。因為我們的物件總是以數字結尾,而不是乙個隨機的字串。從某些方面來說,這是一種隱性的預分片。
對於小數字怎麼處理?比如object:2,我們採用object:作為key,所有剩下的數字作為乙個字段。所以object:2和object:10都會被儲存到key為object:的雜湊表中,但是乙個使用2作為字段,乙個使用10作為字段。
這種方式將節省多少記憶體?
我使用了下面的ruby程式進行了測試:
require 'rubygems'
require 'redis'
useoptimization = true
def hash_get_key_field(key)
s = key.split(":")
if s[1].length > 2
else
endenddef hash_set(r,key,value)
kf = hash_get_key_field(key)
r.hset(kf[:key],kf[:field],value)
enddef hash_get(r,key,value)
kf = hash_get_key_field(key)
r.hget(kf[:key],kf[:field],value)
endr = redis.new
(0..100000).each"
if useoptimization
hash_set(r,key,"val")
else
r.set(key,"val")
end}
在redis2.2的64位版本上測試結果:
從結果看出,這是乙個數量級的優化,我認為這種優化使redis成為最出色的鍵值快取。
特別提示: 要使上面的程式較好的工作,別忘記設定你的redis:
hash-max-zipmap-entries 256
相應的最大鍵值長度設定:
hash-max-zipmap-value 1024
每次雜湊表的元素數量或者值超過了閾值,雜湊將被擴充套件為一張真正的雜湊表進行儲存,此時節約儲存的優勢就沒有了.
或許你想問,你為什麼不自動將這些key進行轉化以提高記憶體利用率?有兩個原因:第一是因為我們更傾向於讓這些權衡明確,而且必須在很多事情之間權衡:cpu,記憶體,最大元素大小限制。第二是頂級的鍵空間支援很多有趣的特性,比如過期,lru演算法,所以這種做法並不是一種通用的方法.
redis的一貫風格是使用者必須理解它是如何運作的,必須能夠做出最好的選擇和權衡,並且清楚它精確的執行方式.
為了儲存使用者資料,當設定了maxmemory後redis會分配幾乎和maxmemory一樣大的記憶體(然而也有可能還會有其他方面的一些記憶體分配).
精確的值可以在配置檔案中設定,或者在啟動後通過 config set 命令設定(see using memory as an lru cache for more info). redis記憶體管理方面,你需要注意以下幾點:
上面的一點意味著,你應該基於你可能會用到的最大記憶體來指定redis的最大記憶體。如果你的程式時不時的需要10g記憶體,即便在大多數情況是使用5g記憶體,你也需要指定最大記憶體為10g.
記憶體分配器是智慧型的,可以復用使用者已經釋放的記憶體。所以當使用的記憶體從5g降低到3g時,你可以重新新增更多的key,而不需要再向作業系統申請記憶體。分配器將復用之前已經釋放的2g記憶體.
因為這些,當redis的peak記憶體非常高於平時的記憶體使用時,碎片所佔可用記憶體的比例就會波動很大。當前使用的記憶體除以實際使用的物理記憶體(rss)就是fragmentation;因為rss就是peak memory,所以當大部分key被釋放的時候,此時記憶體的mem_used / rss
就比較高.
如果maxmemory
沒有設定,redis就會一直向os申請記憶體,直到os的所有記憶體都被使用完。所以通常建議設定上redis的記憶體限制。或許你也想設定maxmemory-policy
的值為noeviction
(在redis的某些老版本預設 並 不是這樣)
設定了maxmemory後,當redis的記憶體達到記憶體限制後,再向redis傳送寫指令,會返回乙個記憶體耗盡的錯誤。錯誤通常會觸發乙個應用程式錯誤,但是不會導致整台機器宕掉.
更多技巧後續推出.
Redis 記憶體優化
非常感謝 redis內部有很多的資料型別,這些在官方文件上都可以看到,下面是其內部優化的一些細節點 1.string 和 數字,在redis中如果儲存的是 123 redis是能夠識別出來這是乙個數字並且按照數字來儲存,節省儲存空間,當然除了這個優化之外,redis內部會構建乙個數字池,預設是100...
redis記憶體優化
在資料量小或者只有int資料的時候,redis採用更緊湊的資料結構來儲存資料 zset hash ziplist set int intset 這些特殊的資料結構是一整塊記憶體,避免來記憶體分片,節省了記憶體,並且增加了cache命中,所以雖然演算法複雜度變成了on,但其實更快了 對於只需要flag...
Redis記憶體優化
redis可以當做記憶體使用,但是如果一直往裡儲存不刪除資料,則必然導致記憶體溢位.想法 如何可以實現記憶體資料的維護呢?可以讓使用者永遠都可以存資料.lru是least recently used的縮寫,即最近最少使用,是一種常用的頁面置換演算法,選擇最近最久未使用的頁面予以淘汰。該演算法賦予每個...