Redis內存優化

內存分配

討論Redis內存優化以前,有必要先聊下關於物理內存、虛擬內存的概念。html

物理內存:就是主機安裝的物理內存條,通常32位的CPU有32條地址線,最大尋址空間就是4G,而64位的CPU則可以支持更大的內存。但是,再大的內存,也有可能知足不了全部應用的內存須要;又或者是,大量使用頻率極低的冷數據佔用了大部分物理內存。這個時候,就是「虛擬內存」出場了。linux

虛擬內存(簡稱VM):由操做系統OS管理,本質就是硬盤中的一個特殊格的磁盤分區、或者一個文件(linux下是swap分區、windows下是ib3文件),其寫入寫出由OS掌管,若是出現以上兩種情景,OS會將溢出物理內存部分數據或者部分冷數據交換到虛擬內存,釋放更多空間提供其餘應用運做。而對於全部應用而言,其運行可以使用的內存理論上是:物理內存 + 虛擬內存redis

Redis跟全部的應用同樣,其內存分配都是要由OS來管理。用戶增長key-value對象,Redis則向OS申請內存劃分到應用;反之,用戶移除key-value對象,Redis向OS返還內存,並由OS決定該將熱數據放入物理內存,冷數據交換到硬盤。算法

不同的是,Redis有本身的內存分配器,當key-value對象被移除時,Redis不會立刻向操做系統釋放其佔用內存(例如,當用戶往一個實例填充了5G的數據,移除其中2G數據,但佔用內存可能仍會保持在5G左右)。爲何Redis要這樣處理?有兩個緣由:windows

一、OS可能會將釋放內存交換到VM,但OS的VM又是物理文件,其IO讀寫效率較低,從而影響Redis性能表現;緩存

二、OS的VM換入換出是基於Page機制,同一Page內的部分數據對象被釋放,但其餘數據對象依然被其餘應用使用中,致使在該Page內的Redis對象沒有被釋放。服務器

而Redis做者應該是考慮到以上問題,不但願Redis由此下降性能,因此在設計上Redis更傾向於本身掌控VM換入的粒度。數據結構

內存監控

redis-cli > info

圖片描述

啓動redis-cli,info命令能夠觀察Redis實例的運行狀況,其中# Memory塊查看內存使用狀況。dom

提示:以上used_memory數據所包含的內存均包含有:分佈式

  • 用戶定義的數據:內存被用來存儲key-value值。

  • 內部開銷: 存儲內部Redis信息用來表示不一樣的數據類型。

再來看看具體每一個數據所指具體含義

  • used_memory:當前Redis全部key-value值及內部開銷理論上要佔用的內存

  • used_memory_human:上一數據的可讀版本

  • used_memory_rss:(Resident Set Size常駐數據集大小),可理解爲OS爲Redis分配的物理內存總量

  • used_memory_peak:峯值內存

  • used_memory_peak_human:峯值內存可讀版本

  • used_memory_lua:lua引擎佔用內存

  • mem_fragmentation_ratio:內存碎片率,used_memory_rss 和 used_memory 之間的比率(如下有更多說明)

  • mem_allocator: Redis 所使用的內存分配器。能夠是 libc 、 jemalloc 或者 tcmalloc

其中mem_fragmentation_ratio(內存碎片率)是分析Redis性能的重要數據指標,計算公式是:

圖片描述

大於1:OS爲Redis分配的物理內存 > Redis全部key-value值及內部開銷應占用的內存

產生緣由:物理內存多出的部分,Redis內移除對象的佔用內存,但這部份內存由Redis自帶內存分配器佔用,沒有向操做系統返回。這一部分就是內存碎片。

小於1:OS爲Redis分配的物理內存 < Redis全部key-value值及內部開銷理應占用的內存

產生緣由:應占內存比物理內存多出的部分,是被操做系統交換到虛擬內存,說明當前Redis的內存使用已經超出物理內存

問題是,這個ratio比率,多少比值最合理?回顧前文,Redis是自帶內存分配器的,自帶內存分配器會保留內部已釋放的內存,目的是爲了不操做系統低性能交換,固然它也是比較靈活的,被釋放的內存塊是能夠被重用。翻閱了下官方文檔,也有對應的說明

  • However allocators are smart and are able to reuse free chunks of memory, so after you freed 2GB of your 5GB data set, when you start adding more keys again, you’ll see the RSS (Resident Set Size) to stay steady and don’t grow more, as you add up to 2GB of additional keys. The allocator is basically trying to reuse the 2GB of memory previously (logically) freed.

所以,內存碎片率保持在1.0至1.5之間是最理想的狀態。倘若碎片率超過了1.5,我所知道的最有效解決手段就是重啓Redis服務器,釋放內存回到操做系統;反之,若碎片率爲0.9,說明物理內存已不夠用,應增添硬件,或設置Redis最大內存限制maxmemory

最大內存限制maxmemory的設置很是重要,若是不設置maxmemory,Redis一直會爲其分配內存,直至耗盡全部物理內存,直到操做系統進行虛擬內存交換。所以,通常狀況下,做者建議仍是把峯值設置設上。開啓此配置,當超出限定內存狀況發生,Redis會返回異常消息,操做系統不會因內存溢出而奔潰。還有一點建議是,開發者在系統設計之初,就應當制定Redis內存使用劃分計劃,而劃分原則是,爲Redis準備系統可能使用的峯值內存,而不是平均使用內存。例如系統大部分狀況會以Redis做爲分佈式緩存寫入10G數據,但大部分狀況下只會跑到4G,但Redis依然推薦用戶爲其預留10G內存(used_memory_peak峯值)。

redis.conf--> maxmemory 10000000

maxmemory的單位是bytes,默認爲0,即不限制最大內存。

內存限制與Key回收

maxmemory之外,仍然須要指定Redis在最大內存溢出後的處理行爲——maxmemory-policy。同時設置了maxmemorymaxmemory-policy選項,redis內存使用達到上限。能夠經過設置LRU算法(Least Recently Used 近期最少使用算法)來刪除部分key,釋放空間。相關內容參考:將redis當作使用LRU算法的緩存來使用

提示:Redis32位實例最大可用內存爲3G,64位則無限制,而RDB與AOF持久化文件都兼容支持32位或64位實例,所以能夠自由切換在32位與64位之間切換。

volatile-lru -> 根據LRU算法生成的過時時間來刪除。
allkeys-lru -> 根據LRU算法刪除任何key。
volatile-random -> 根據過時設置來隨機刪除key。
allkeys-random -> 無差異隨機刪。
volatile-ttl -> 根據最近過時時間來刪除(輔以TTL)
noeviction -> 誰也不刪,直接在寫操做時返回錯誤。

String與Hash

String與Hash毫無疑問是最經常使用的兩種數據結構,並且AB兩個方案互換使用每每均可以知足系統的業務需求,該如何選型,才能在複雜度、性能以及內存間取得一個平衡點?

舉個例子,存儲如下數據,分別採起String與Hash的形式:

  • object:1

  • object:2

  • object:3

  • object:……

  • object:100000

String存儲:以object:x做爲key,(1)

stringexp

Hash存儲:以object:x做爲Key,後兩位字節xx做爲hash key,(2)

hash100exp

(1)存儲結果:
string10w

(2)存儲結果:
hash100-10w

從以上結果能夠得出:hash散列比string更節省內存,緣由有3點:

  1. Redis的Key承載了不少特性的,過時時間、LRU、Cluster節點信息、系統關鍵信息等,所以和String類型的key-value相比,hash類型可更大限度地減小key的數量,從而節省內存空間的使用率;

  2. key對應多種數據結構,而hash key對應的value只能是string,這種簡單結構比多重複雜結構更加高效。(Redis是不支持嵌套數據的,例如hash中value還嵌套有hash);

  3. hash在額定數量及容積下,將會以一維線性的緊湊格式存儲(ziplist),這種存儲形式可節省更多空間。

注意一點是:hash只會在額定的要求範圍內,纔會以ziplist存儲,當超過額定閾值後會轉換成真正hashtable格式並從新存儲。而hashtable的存儲對於內存的使用不佔優點。關於這個額定值,redis提供了兩個可配置參數hash-max-ziplist-entrieshash-max-ziplist-value

  • hash-max-ziplist-entries:hash以ziplist存儲的最大對象數量,單位:個

  • hash-max-ziplist-value:hash以ziplist存儲的單個對象佔用空間,單位:bytes

默認值是:

# Hashes are encoded using a memory efficient data structure when they have a
# small number of entries, and the biggest entry does not exceed a given
# threshold. These thresholds can be configured using the following directives.
hash-max-ziplist-entries 512
hash-max-ziplist-value 64

修改配置hash-max-ziplist-entries爲20萬,再調整以上例子寫入10萬數據到單一散列中,

advconfig20w

結果是,與(2)例子接近,以ziplist格式存儲更節省空間,但缺點是ziplist格式讀寫速度至關低效,10萬數據的寫入花費了99356毫秒,接近1.5分鐘。所以,在實際應用中,對應存儲格式的選型,仍是須要根據實際需求做爲衡量標準。

onehash10w

相關文章
相關標籤/搜索