關於記憶體管理優化,本文主要和大家分享Redis優化經驗總結,希望能幫助大家。
Redis Hash是value內部為一個HashMap,如果該Map的成員數比較少,則會採用類似一維線性的緊湊格式來存儲該Map, 即省去了大量指針的內存開銷,這個參數控制對應在redis.conf設定檔中下面2項:
hash-max-zipmap-entries 64 hash-max-zipmap-value 512
當value這個Map內部不超過多少個成員時會採用線性緊湊格式存儲,預設是64,即value內部有64個以下的成員就是使用線性緊湊存儲,超過該值自動轉成真正的HashMap。
hash-max-zipmap-value 意義是當 value這個Map內部的每個成員值長度不超過多少位元組就會採用線性緊湊儲存來節省空間。
以上2個條件任一個條件超過設定值都會轉換成真正的HashMap,也就不會再節省記憶體了,那麼這個值是不是設定的越大越好呢,答案當然是否定的, HashMap的優勢就是查找和操作的時間複雜度都是O(1)的,而放棄Hash採用一維存儲則是O(n)的時間複雜度,如果
成員數量很少,則影響不大,否則會嚴重影響效能,所以要權衡好這個值的設置,整體上還是最根本的時間成本和空間成本上的權衡。
list-max-ziplist-value 64 list-max-ziplist-entries 512
list資料類型節點值大小小於多少位元組會採用緊湊儲存格式、list資料型別多少節點會採用去指標的緊湊儲存格式。
記憶體預先分配:
Redis內部實作沒有對記憶體分配方面做過多的最佳化(對比Memcache),在一定程度上會存在記憶體碎片,不過大多數情況下這個不會成為Redis的效能瓶頸,不過如果在Redis內部儲存的大部分資料是數值型的話,Redis內部採用了一個shared integer的方式來省去分配記憶體的開銷,即在系統啟動時先分配一個從1~ n 那麼多個數值對象放在一個池子中,如果存儲的數據恰好是這個數值範圍內的數據,則直接從池子裡取出該對象,並且通過引用計數的方式來共享,這樣在系統存儲了大量數值下,也能一定程度上節省記憶體並且提高效能,這個參數值n的設定需要修改原始碼中的一行巨集定義REDIS_SHARED_INTEGERS,這個值預設是10000,可以依照自己的需求進行修改,修改後重新編譯就可以了。
持久化機制:
定時快照方式(snapshot):
該持久化方式實際上是在Redis內部一個定時器事件,每隔固定時間去檢查當前資料發生的改變次數與時間是否滿足配置的持久化觸發的條件,如果滿足則透過作業系統fork呼叫來建立出一個子進程,這個子進程預設會與父進程共享相同的位址空間,這時就可以透過子進程來遍歷整個內存來進行存儲操作,而主進程則仍然可以提供服務,當有寫入時由操作系統按照內存頁(page)為單位來進行copy-on-write保證父子進程之間不會互相影響。
這個持久的主要缺點是定時快照只是代表一段時間內的記憶體映像,所以系統重新啟動會遺失上次快照與重新啟動之間所有的資料。
基於語句追加方式(aof):
aof方式實際類似mysql的基於語句的binlog方式,即每條會使Redis內存資料改變的命令都會追加到一個log文件中,也就是說這個log檔案就是Redis的持久化資料。
aof的方式的主要缺點是追加log檔案可能導致體積過大,當系統重啟恢復資料時如果是aof的方式則加載資料會非常慢,幾十G的資料可能需要幾小時才能載入完,當然這個耗時並不是因為磁碟檔案讀取速度慢,而是由於讀取的所有指令都要在記憶體中執行一遍。另外由於每個指令都要寫log,所以使用aof的方式,Redis的讀寫效能也會有所下降。
可以考慮將資料保存到不同的Redis實例中,每個實例的記憶體大小在2G左右,避免將雞蛋放到一個籃子裡,既可以減少快取失效對系統帶來的影響,又可以加快資料恢復的速度,不過同時也為系統設計帶來了一定的複雜性。
Redis持久化崩潰問題:
有Redis線上維運經驗的人會發現Redis在物理記憶體使用比較多,但還沒有超過實際物理記憶體總容量時就會發生不穩定甚至崩潰的問題,有人認為是基於快照方式持久化的fork系統調用造成內存佔用加倍而導致的,這種觀點是不准確的,因為fork 調用的copy-on-write機制是基於操作系統頁這個單位的,也就是只有有寫入的髒頁會被複製,但是一般你的系統不會在短時間內所有的頁都發生了寫入而導致複製,那麼是什麼原因導致Redis崩潰的呢?
答案是Redis的持久化使用了Buffer IO造成的,所謂Buffer IO是指Redis對持久化檔案的寫入和讀取操作都會使用物理記憶體的Page Cache,而大多數資料庫系統會使用Direct IO來繞過這層Page Cache並自行維護一個資料的Cache,而當Redis的持久化檔案過大(尤其是快照檔案),並對其進行讀寫時,磁碟檔案中的資料都會被載入到實體內存中作為作業系統對該檔案的一層Cache,而這層Cache的資料與Redis記憶體中管理的資料實際上是重複儲存的,雖然核心在物理記憶體緊張時會做Page Cache的剔除工作,但核心很可能認為某塊Page Cache更重要,而讓你的進程開始Swap ,這時你的系統就會開始出現不穩定或崩潰了。我們的經驗是當你的Redis實體記憶體使用超過記憶體總容量的3/5時就會開始比較危險了。
總結:
1、根據業務需求選擇合適的資料類型,並為不同的應用場景設定對應的緊湊儲存參數。
2、當業務場景不需要資料持久化時,關閉所有的持久化方式可以獲得最佳的效能以及最大的記憶體使用量。
3、如果需要使用持久化,根據是否可以容忍重啟丟失部分資料在快照方式與語句追加方式之間選擇其一,不要使用虛擬記憶體以及diskstore方式。
4、不要讓你的Redis所在機器物理記憶體使用超過實際記憶體總量的3/5。
redis.conf中的maxmemory選項,該選項是告訴Redis當使用了多少物理內存後就開始拒絕後續的寫入請求,該參數能很好的保護好你的Redis不會因為使用了過多的實體記憶體而導致swap,最終嚴重影響效能甚至崩潰。
redis.conf檔中vm-enabled 為no
相關推薦:
以上是Redis優化經驗總結的詳細內容。更多資訊請關注PHP中文網其他相關文章!