99热99这里只有精品6国产,亚洲中文字幕在线天天更新,在线观看亚洲精品国产福利片 ,久久久久综合网

歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋果6袋
6
麥子學(xué)院

Redis學(xué)習(xí)之內(nèi)存優(yōu)化詳解

發(fā)布時(shí)間:2017-06-24 13:16  回復(fù):0  查看:2024   最后回復(fù):2017-06-24 13:16  
本文和大家分享的主要是redis 中內(nèi)存優(yōu)化相關(guān)內(nèi)容,一起來看看吧,希望對(duì)大家 學(xué)習(xí)redis有所幫助。
   小的聚合類型數(shù)據(jù)的特殊編碼處理
  Redis2.2 版本及以后,存儲(chǔ)集合數(shù)據(jù)的時(shí)候會(huì)采用內(nèi)存壓縮技術(shù),以使用更少的內(nèi)存存儲(chǔ)更多的數(shù)據(jù)。如 Hashes,Lists,Sets Sorted Sets ,當(dāng)這些集合中的所有數(shù)都小于一個(gè)給定的元素,并且集合中元素?cái)?shù)量小于某個(gè)值時(shí),存儲(chǔ)的數(shù)據(jù)會(huì)被以一種非常節(jié)省內(nèi)存的方式進(jìn)行編碼,使用這種編碼理論上至少會(huì)節(jié)省 10 倍以上內(nèi)存(平均節(jié)省 5 倍以上內(nèi)存)。并且這種編碼技術(shù)對(duì)用戶和 redis api 透明。因?yàn)槭褂眠@種編碼是用 CPU 換內(nèi)存,所以我們提供了更改閾值的方法,只需在 redis.conf 里面進(jìn)行修改即可 .
  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
 ?。现校┤绻硞€(gè)值超過了配置文件中設(shè)置的最大值,redis 將自動(dòng)把把它(集合)轉(zhuǎn)換為正常的散列表。這種操作對(duì)于比較小的數(shù)值是非??斓?,但是,如果你為了使用這種編碼技術(shù)而把配置進(jìn)行了更改,你最好做一下基準(zhǔn)測(cè)試(和正常的不采用編碼做一下對(duì)比) .
   使用32位的redis
  使用32 位的 redis ,對(duì)于每一個(gè) key, 將使用更少的內(nèi)存,因?yàn)?/span> 32 位程序,指針占用的字節(jié)數(shù)更少。但是 32 redis 整個(gè)實(shí)例使用的內(nèi)存將被限制在 4G 以下。使用 make 32bit 命令編譯生成 32 位的 redis 。 RDB AOF 文件是不區(qū)分 32 位和 64 位的(包括字節(jié)順序) , 所以你可以使用 64 位的 reidis 恢復(fù) 32 位的 RDB 備份文件,相反亦然 .
   位級(jí)別和字級(jí)別的操作
  Redis 2.2 引入了位級(jí)別和字級(jí)別的操作 :GETRANGE,  SETRANGE ,GETBIT  SETBIT. 使用這些命令,那你可以把 redis 的字符串當(dāng)做一個(gè)隨機(jī)讀取的數(shù)組。例如你有一個(gè)應(yīng)用,用來標(biāo)志用戶的 ID 是連續(xù)的整數(shù),你可以使用一個(gè)位圖標(biāo)記用戶的性別,使用 1 表示男性, 0 表示女性,或者其他的方式。這樣的話, 1 億個(gè)用戶將僅使用 12 M 的內(nèi)存。你可以使用同樣的方法,使用 GETRANGE  SETRANGE 命令為每個(gè)用戶存儲(chǔ)一個(gè)字節(jié)的信息。這僅是一個(gè)例子,實(shí)際上你可以使用這些原始數(shù)據(jù)類型解決更多問題
   盡可能使用散列表(hashes
  小散列表(是說散列表里面存儲(chǔ)的數(shù)少)使用的內(nèi)存非常小,所以你應(yīng)該盡可能的將你的數(shù)據(jù)模型抽象到一個(gè)散列表里面。比如你的web 系統(tǒng)中有一個(gè)用戶對(duì)象,不要為這個(gè)用戶的名稱,姓氏,郵箱,密碼設(shè)置單獨(dú)的 key, 而是應(yīng)該把這個(gè)用戶的所有信息存儲(chǔ)到一張散列表里面 .
  如果你想了解更多關(guān)于這方面的知識(shí),請(qǐng)讀下一段.
   使用散列結(jié)構(gòu)高效存儲(chǔ)抽象的鍵值對(duì)
  我知道這部分的標(biāo)題很嚇人,但是我將詳細(xì)的解釋這部分內(nèi)容.
  一般而言,把一個(gè)模型(model )表示為 key-value 的形式存儲(chǔ)在 redis 中非常容易,當(dāng)然 value 必須為字符串,這樣存儲(chǔ)不僅比一般的 key value 存儲(chǔ)高效,并且比 memcached 存儲(chǔ)還高效 .
  讓我們做個(gè)對(duì)比:一些key 存儲(chǔ)了一個(gè)對(duì)象的多個(gè)字段要比一個(gè)散列表存儲(chǔ)對(duì)象的多個(gè)字段占用更多的內(nèi)存。這怎么可能?從原理上講,為了保證查找一個(gè)數(shù)據(jù)總是在一個(gè)常量時(shí)間內(nèi)( O(1) , 需要一個(gè)常量時(shí)間復(fù)雜度的數(shù)據(jù)結(jié)構(gòu),比如說散列表 .
  但是,通常情況下,散列表只包括極少的幾個(gè)字段。當(dāng)散列表非常小的時(shí)候,我們采用將數(shù)據(jù)encode 為一個(gè) O(N) 的數(shù)據(jù)結(jié)構(gòu),你可以認(rèn)為這是一個(gè)帶有長度屬性的線性數(shù)組。只有當(dāng) N 是比較小的時(shí)候,才會(huì)采用這種 encode ,這樣使用 HGET HSET 命令的復(fù)雜度仍然是 O(1) :當(dāng)散列表包含的元素增長太多的時(shí)候,散列表將被轉(zhuǎn)換為正常的散列表(極限值可以在 redis.conf 進(jìn)行配置) .
  無論是從時(shí)間復(fù)雜度還是從常量時(shí)間的角度來看,采用這種encode 理論上都不會(huì)有多大性能提升,但是,一個(gè)線性數(shù)組通常會(huì)被 CPU 的緩存更好的命中(線性數(shù)組有更好的局部性) , 從而提升了訪問的速度 .
  既然散列表的字段及其對(duì)應(yīng)的值并不是用redis objects 表示,所以散列表的字段不能像普通的 key 一樣設(shè)置過期時(shí)間。但是這毫不影響對(duì)散列表的使用,因?yàn)樯⒘斜肀緛砭褪沁@樣設(shè)計(jì)的(我們相信簡(jiǎn)潔比多功能更重要,所以嵌入對(duì)象是不允許的,散列表字段設(shè)置單獨(dú)的過期時(shí)間是不允許的) .
  所以散列表能高效利用內(nèi)存。這非常有用, 當(dāng)你使用一個(gè)散列表存儲(chǔ)一個(gè)對(duì)象或者抽象其他一類相關(guān)的字段為一個(gè)模型時(shí)。但是,如果我們有一個(gè)普通的 key value 業(yè)務(wù)需求怎么辦 ?
  假如我們想使用redis 存儲(chǔ)許多小對(duì)象,這些對(duì)象可以使用 json 字符串表示,也可能是 HTML 片段和簡(jiǎn)單的 key->boolean 鍵值對(duì)。概況的說,一切皆字符串,都可以使用 string:string 的形式表示 .
  我們假設(shè)要緩存的對(duì)象使用數(shù)字后綴進(jìn)行編碼,如:
  · object:102393
  · object:1234
  · object:5
  我們可以這樣做。每次SET 的時(shí)候,把 key 分為兩部分,第一部分當(dāng)做一個(gè) key ,第二部當(dāng)做散列表字段。比如 “object:1234”, 分成兩部分 :
  · a Key named object:12
  · a Field named 34
  我們使用除最后2 個(gè)數(shù)字的部分作為 key, 最后 2 個(gè)數(shù)字做為散列表的字段。使用命令 :
  HSET  object:12 34 somevalue
  如你所見,每個(gè)散列表將(理論上)包含100 個(gè)字段,這是 CPU 資源和內(nèi)存資源之間的一個(gè)折中 .
  另一個(gè)需要你關(guān)注的是在這種模式下,無論緩存多少對(duì)象,每個(gè)散列表都會(huì)分配100 個(gè)字段。因?yàn)槲覀兊膶?duì)象總是以數(shù)字結(jié)尾,而不是一個(gè)隨機(jī)的字符串。從某些方面來說,這是一種隱性的預(yù)分片。
  對(duì)于小數(shù)字怎么處理?比如object:2, 我們采用 object: 作為 key, 所有剩下的數(shù)字作為一個(gè)字段。所以 object:2 object:10 都會(huì)被存儲(chǔ)到 key object: 的散列表中,但是一個(gè)使用 2 作為字段,一個(gè)使用 10 作為字段。
  這種方式將節(jié)省多少內(nèi)存?
  我使用了下面的Ruby 程序進(jìn)行了測(cè)試 :
   require 'rubygems' require 'redis'
  UseOptimization = true
   def  hash_get_key_field(key)
  s = key.split(":")
   if s[1].length > 2
  {:key => s[0]+":"+s[1][0..-3], :field => s[1][-2..-1]}
   else
  {:key => s[0]+":", :field => s[1]}
   endend
   def  hash_set(r,key,value)
  kf = hash_get_key_field(key)
  r.hset(kf[:key],kf[:field],value) end
   def  hash_get(r,key,value)
  kf = hash_get_key_field(key)
  r.hget(kf[:key],kf[:field],value) end
  r = Redis.new
  (0..100000).each{|id|
  key = "object:#{id}"
   if UseOptimization
  hash_set(r,key,"val")
   else
  r.set(key,"val")
   end
  }
  在redis2.2 64 位版本上測(cè)試結(jié)果 :
  ·  當(dāng)開啟優(yōu)化時(shí)使用內(nèi)存 1.7M
  ·  當(dāng)未開啟優(yōu)化時(shí)使用內(nèi)存 11M
  從結(jié)果看出,這是一個(gè)數(shù)量級(jí)的優(yōu)化,我認(rèn)為這種優(yōu)化使redis 成為最出色的鍵值緩存。
  特別提示 :  要使上面的程序較好的工作,別忘記設(shè)置你的 redis:
  hash-max-zipmap-entries 256
  相應(yīng)的最大鍵值長度設(shè)置:
  hash-max-zipmap-value 1024
  每次散列表的元素?cái)?shù)量或者值超過了閾值,散列將被擴(kuò)展為一張真正的散列表進(jìn)行存儲(chǔ),此時(shí)節(jié)約存儲(chǔ)的優(yōu)勢(shì)就沒有了.
  或許你想問,你為什么不自動(dòng)將這些key 進(jìn)行轉(zhuǎn)化以提高內(nèi)存利用率?有兩個(gè)原因:第一是因?yàn)槲覀兏鼉A向于讓這些權(quán)衡明確,而且必須在很多事情之間權(quán)衡: CPU ,內(nèi)存,最大元素大小限制。第二是頂級(jí)的鍵空間支持很多有趣的特性,比如過期, LRU 算法,所以這種做法并不是一種通用的方法 .
  Redis 的一貫風(fēng)格是用戶必須理解它是如何運(yùn)作的,必須能夠做出最好的選擇和權(quán)衡,并且清楚它精確的運(yùn)行方式 .
   內(nèi)存分配
  為了存儲(chǔ)用戶數(shù)據(jù), 當(dāng)設(shè)置了 maxmemory Redis 會(huì)分配幾乎和 maxmemory 一樣大的內(nèi)存(然而也有可能還會(huì)有其他方面的一些內(nèi)存分配) .
  精確的值可以在配置文件中設(shè)置,或者在啟動(dòng)后通過CONFIG SET  命令設(shè)置 (see  Using memory as an LRU cache for more info). Redis 內(nèi)存管理方面,你需要注意以下幾點(diǎn) :
  ·  當(dāng)某些緩存被刪除后 Redis 并不是總是立即將內(nèi)存歸還給操作系統(tǒng)。這并不是 redis 所特有的,而是函數(shù) malloc() 的特性。例如你緩存了 5G 的數(shù)據(jù),然后刪除了 2G 數(shù)據(jù),從操作系統(tǒng)看, redis 可能仍然占用了 5G 的內(nèi)存(這個(gè)內(nèi)存叫 RSS, 后面會(huì)用到這個(gè)概念),即使 redis 已經(jīng)明確聲明只使用了 3G 的空間。這是因?yàn)?/span> redis 使用的底層內(nèi)存分配器不會(huì)這么簡(jiǎn)單的就把內(nèi)存歸還給操作系統(tǒng),可能是因?yàn)橐呀?jīng)刪除的 key 和沒有刪除的 key 在同一個(gè)頁面( page , 這樣就不能把完整的一頁歸還給操作系統(tǒng) .
  ·  上面的一點(diǎn)意味著,你應(yīng)該基于你可能會(huì)用到的  最大內(nèi)存  來指定redis 的最大內(nèi)存。如果你的程序時(shí)不時(shí)的需要 10G 內(nèi)存,即便在大多數(shù)情況是使用 5G 內(nèi)存,你也需要指定最大內(nèi)存為 10G.
  ·  內(nèi)存分配器是智能的,可以復(fù)用用戶已經(jīng)釋放的內(nèi)存。所以當(dāng)使用的內(nèi)存從 5G 降低到 3G 時(shí),你可以重新添加更多的 key ,而不需要再向操作系統(tǒng)申請(qǐng)內(nèi)存。分配器將復(fù)用之前已經(jīng)釋放的 2G 內(nèi)存 .
  ·  因?yàn)檫@些,當(dāng) redis peak 內(nèi)存非常高于平時(shí)的內(nèi)存使用時(shí),碎片所占可用內(nèi)存的比例就會(huì)波動(dòng)很大。當(dāng)前使用的內(nèi)存除以實(shí)際使用的物理內(nèi)存( RSS )就是 fragmentation ;因?yàn)?/span> RSS 就是 peak memory ,所以當(dāng)大部分 key 被釋放的時(shí)候,此時(shí)內(nèi)存的  mem_used / RSS 就比較高.
  如果  maxmemory  沒有設(shè)置,redis 就會(huì)一直向 OS 申請(qǐng)內(nèi)存,直到 OS 的所有內(nèi)存都被使用完。所以通常建議設(shè)置上 redis 的內(nèi)存限制?;蛟S你也想設(shè)置  maxmemory-policy  的值為  noeviction (在redis 的某些老版本默認(rèn) 并 不是這樣)
  設(shè)置了maxmemory 后,當(dāng) redis 的內(nèi)存達(dá)到內(nèi)存限制后,再向 redis 發(fā)送寫指令,會(huì)返回一個(gè)內(nèi)存耗盡的錯(cuò)誤。錯(cuò)誤通常會(huì)觸發(fā)一個(gè)應(yīng)用程序錯(cuò)誤,但是不會(huì)導(dǎo)致整臺(tái)機(jī)器宕掉 .
來源:極客頭條

您還未登錄,請(qǐng)先登錄

熱門帖子

最新帖子

?