《深刻理解redis》之三:內存管理的建議與技巧

配置redis

若是想要運行一個內存高效的redis數據庫,首先要理解那些在 redis.conf 配置文件中全部內存相關的指令。node

 redis.conf 爲大多數指令提供了豐富的內聯文檔,大多數redis配置指令能夠在運行時經過CONFIG SET進行設置。redis

rdbchecksum ,默認是yes,將65位循環冗餘校驗碼(CRC64)放置在RDB快照文件的末尾,做爲防止文件損壞的一種手段。當redis產生一個子進程將快照保存至磁盤時,對RDB快照進行CRC64校驗會增長10%的內存使用。算法

redis中的主哈希表將主鍵與對應的值進行關聯,若activerehashing=yes,那麼該主哈希表每一個100毫秒會從新哈希。從新哈希的過程將釋放刪除了的鍵佔用的操做系統內存。activerehashing 發生在非工做時間,對客戶端鏈接額影響最小。數據庫

主從複製

採用主從複製的方式提供高度可靠性、可伸縮性。在集羣環境中,經過slave-of 指令將redis實例切換到從模式,此時 從實例 被容許從另外一個被指派爲主實例的redis中複製數據。內存 和 來自硬件和網絡的延遲會直接影響主從實例的性能。數組

repl-disable-tcp-nodelay 指令能夠用來更好地處理redis主從實例間的網絡流量擁塞。經過對主從間的密集數據同步和更少的網絡流量之間進行權衡,這種複製可以改善高流量狀況下的網絡性能。緩存

32位redis

在redis.io網站上的官方文檔內存優化中,其中有一條建議是在32位模式下編譯redis替代默認的64位實例。對於一樣小於3GB的數據集,其在32位redis實例中要比在64位版本中的redis中小。網絡

若是應用程序使用整數集合,只要總內存不超過4GB的最大限制,那麼32位版本節省的內存是至關可觀的。當存入不一樣大小和類型的值時,redis的32位和64位版本之間的百分比差別意味着節省的內存數量顯著下降。對於那些使用集合的應用程序來講,它們較多的使用了字符串,由於64位的redis多是更好的選擇。這是由於在64位版本中的字符串擁有額外的空間及更高效的編碼。數據結構

對於哈希數據結構來講,32位和64位redis實例並無特別大的差別:app

對於32位版本的redis列表來講,在32位限制之下,列表很是適合存儲整數和浮點數:dom

對於redis集合的測試以下:

1) 32位的redis並未在redis用戶羣中進行普遍部署和測試,所以相較於64位版本可能會有未發現的bug。

2) 諸如bittop、bitcount這樣的位操做基於redis的64位版本進行了優化,所以相較於32位則沒有那麼高效。

3) 在32位redis中設置maxmemory參數更加困難,若是在32位版本的redis中的maxmemory值設置的過於靠近最大值4GB,那麼通訊、主從複製、I/O緩存都有可能隨時致使redis崩潰。

info meory

10.143.128.165:6379> info memory
# Memory
used_memory:819312
used_memory_human:800.11K
used_memory_rss:1904640
used_memory_peak:2282784
used_memory_peak_human:2.18M
used_memory_lua:36864
mem_fragmentation_ratio:2.32
mem_allocator:jemalloc-3.6.0

used_memory    分配的字節數大小

used_memory_human 將used_memory格式化爲人類可讀的值

used_memory_rss    常駐集大小(resident set size) 操做系統中看到的內存分配, 

                                以及top顯示的結果

used_memory_peak    redis使用的峯值內存

used_memory_peak_human    將used_memory_peak可視化

used_memory_lua                    redis的lua子系統使用的字節數

mem_fragmentation_ratio        used_memory_rss與used_memory的比率

mem_allocator                        在編譯器redis使用的分配器

鍵過時

保證redis數據庫不會超過可用內存---爲鍵設置超時時間,一旦過了鍵的超時時限,鍵就會被自動驅逐。

當在鍵上調用EXPIRE命令設置過時時間時,該超時只能經過刪除或者替換鍵的方式清除。以後,任何改變值的命令都是沒法更改或者清楚以前設置的超時的。

10.143.128.165:6379> set name yinn
OK
10.143.128.165:6379> expire name 60
(integer) 1
10.143.128.165:6379> get name
"yinn"
10.143.128.165:6379> ttl name
(integer) 53
10.143.128.165:6379> append name nana
(integer) 8
10.143.128.165:6379> get name
"yinnnana"
10.143.128.165:6379> get name
(nil)

若是在設置了超時的鍵上調用set或者getset(鍵過時以前),那麼超時會被清除,鍵不會從數據庫中驅逐:

10.143.128.165:6379> set name yin
OK
10.143.128.165:6379> expire name 30
(integer) 1
10.143.128.165:6379> set name xian
OK
10.143.128.165:6379> ttl name
(integer) -1

也可使用 persist 命令清楚鍵上的超時設定:

10.143.128.165:6379> set name yinn
OK
10.143.128.165:6379> expire name 60
(integer) 1
10.143.128.165:6379> ttl name
(integer) 51
10.143.128.165:6379> persist name
(integer) 1
10.143.128.165:6379> ttl name
(integer) -1

在一個已經設置過超時的鍵上調用expire命令將會清楚並從新設定超時:

10.143.128.165:6379> expire name 90
(integer) 1
10.143.128.165:6379> ttl name
(integer) 87
10.143.128.165:6379> expire name 20
(integer) 1
10.143.128.165:6379> ttl name
(integer) 17
10.143.128.165:6379> get name
(nil)

LRU鍵驅逐策略

經過maxmemory指令將最大內存設置爲1MB以建立一個小內存的redis實例。maxmemory指令容許設定內存大小的硬性上限,運行時的redis實例受限於此。

10.143.128.165:6379> flushall   ##清楚數據庫
OK
10.143.128.165:6379>config set maxmemory 1024
OK

當redis內存耗盡時,默認生效的是永不過時策略(noeviction policy):

默認的maxmemory-policy策略是永不過時。在noeviction策略中,沒有鍵設置爲過時。若是redis沒有可用內存,任何寫操做都會致使redis錯誤。

10.143.128.165:6379> config get maxmemory
1) "maxmemory"
2) "1024
10.143.128.165:6379>config get maxmemory-policy
1) "maxmemory-policy"
2) "noeviction"

第一種過時LRU策略名爲 volatile-lru,它將最近較少使用的鍵驅逐出去。這些鍵必須經過 expire set 命令設置了超時的。當redis內存耗盡時,redis開始刪除那些設置了過時時間的鍵,即使該鍵仍然有剩餘時間。

下一個策略是 allkeys-lru ,會刪除redis中任何一個鍵,並且沒有辦法限制哪些鍵被刪除。

redis的LRU算法是不許確的,由於redis並不會自動選擇最佳的候選鍵來驅逐,例如最少使用的鍵或者最先訪問的鍵。相反,redis默認行爲是選取5個鍵的samples,並驅逐當中最少使用的那個。若是想要增長LRU算法的精確性,能夠更改redis.conf文件中的maxmemory-samples指令,或者在運行時經過config set maxmemory-samples命令進行設置。

將maxmemory-samples增長到10,從而提高redisLRU算法的性能,效果接近真實LRU算法,可是反作用就是消耗更多的CPU計算能力。將maxmemory-samples降至3,從而減小了redisLRU算法的精確性,不過相應地加快了處理速度。

接下來是兩種最大內存驅逐策略:volatile-random和allkeys-random。它們與volatile-lru和allkeys-lru類似,可是不採用LRU算法。volatile-random 基於鍵上設置的過時狀態隨機驅逐一個鍵,須要O(n)時間複雜度的操做來計算建立的這些鍵是否已被驅逐對於allkeys-random,整個鍵空間都是開放的。

最後一個最大內存策略是volatile-ttl,redis會嘗試根據鍵的剩餘時間(TTL)清除鍵。

建立內存高效的redis數據結構

小巧的哈希、列表、集合、有序集合

對於哈希、列表、有序集合來講,這種特殊編碼方法基於ziplist(壓縮列表):

壓縮列表是一種特殊的雙向鏈表,專爲內存高效進行設計的。它存儲了字符串和整數值,其中整數是以真正的整數值而非字符串形式進行存儲的。它容許在列表的任意一端以O(1)時間複雜度進行push和pop操做。可是,因爲每次操做都須要爲ziplist使用的內存進行從新分配,實際上的複雜度與ziplist所使用的內存大小有關。

根據大小、類型以及數據結構的內容,ziplist編碼方式爲redis數據庫極大地節約了內存使用。

redis中ziplist的實現經過爲每一個節點只存儲3份數據實現較小的內存佔用:第一份是前一個節點的長度,第二份是當前節點的長度,第三份是存儲的值

對於哈希表來講,hash-max-ziplist-entries 指令設置了總共有多少字段是能夠被特殊編碼爲ziplist,默認是512。hash-max-ziplist-value指令設置了從ziplist轉變爲哈希表所要達到的最大大小,默認64.

把位、字節和redis字符串用做隨機訪問數組

在集合中經過使用整數代替字符串,能夠將集合的大小顯著地下降,可是 位圖仍然要比集合小一個數量級、更節省內存。

優化哈希,高效存儲

 

 

 

待續P90

相關文章
相關標籤/搜索