redis學習筆記之內存篇

1.內存消耗:

內存統計:info memory命令

重點關注:used_memory_rss(redis進程佔用內存總量)、used_memory(redis內部存儲數據佔用內存總量)與mem_framentation_ratio(前面2個的比值,指碎片率)

mem_framentation_ratio>1時,說明userd_memory_rss - used_memory多出的部份內存沒用於存儲數據,而是被內存碎片消耗,相差很大則說明碎片率嚴重

mem_fragmentation_ratio<1時,該情況一般出現在操作系統把redis內存交換(Swap)到硬盤導致,此時redis性能會變得很差,甚至僵死

內存消耗 = 自身內存 + 對象內存 + 緩衝內存 + 內存碎片

對象內存 = sizeof(keys) + sizeof(values):內存佔用最大的一塊,以K-V存儲數據,K爲字符串對象,V包含string、hash、list、set、zset

緩存內存 = 客戶端緩衝+複製積壓緩衝區+aof緩衝區(前面有,不做解釋)

內存碎片:redis分配策略一般採用固定範圍的內存塊進行分配,每個範圍又劃分爲多個小的內存塊單元,如:jemalloc將內存如此劃分

    小:[8byte],[16byte,32byte,48byte,...,128byte],[192byte,256byte,...,512byte],[768byte,1024byte,...,3840byte]

    大:[4KB,8KB,12KB,...,4072KB]

    巨大:[4MB,8MB,12MB,...]

如:保存10kb的數據可能採用12kb的塊存儲,剩下2kb就是碎片,不再分配 redis有jemalloc、glibc與tcmalloc三種分配策略(沒去深究),正常碎片率是1.03左右

易出現高內存碎片場景:頻繁對已存在的鍵執行append、setrange等更新操作、大量過期鍵刪除,鍵對象刪除後,釋放的空間無法充分利用(這個一直想不通)

內存碎片解決方式:

a.數據對齊:條件允許下儘量做數據對齊,如採用數字或固定長度字符串

b.安全重啓:重啓節點可做到內存碎片重新整理,將碎片率過高的主節點轉換爲從節點,進行安全重啓

子線程內存消耗:主要是執行aof/rdb重寫時redis創建的子進程內存消耗,高併發場景下應關閉linux的thp機制,否則子進程內存消耗可能是父進程的數倍;需設置sysctl vm overcommit_memory=1允許內核可以分配所有的物理內存,防止redis進程執行fork操作時因系統剩餘內存不足而失敗

2.內存管理:

設置內存上限:可用maxmemory參數限制最大可用內存, 當內存上限達到maxmemory時使用lru等刪除策略釋放空間,該參數限制的是userd_memory統計項對應的內存,由於內存碎片的存在,實際消耗的內存可能會比maxmemory設置的大,小心溢出

動態調整內存上限:config set maxmemory xxx 命令

內存回收策略:刪除達到過期時間的鍵對象、內存使用達到maxmemory上限時觸發內存溢出控制策略

刪除達到過期時間的鍵對象(惰性刪除與定時刪除):

惰性刪除:前面介紹了

定時刪除: redis內部維護一個定時任務,默認每秒運行10次,根據鍵的過期比例、使用快慢2種速率模式回收鍵,默認採用慢模式運行

a.定時任務在每個數據庫空間隨機檢查20個鍵,當發現過 期時刪除對應的鍵

b.如果超過檢查數25%的鍵過期,循環執行回收邏輯直到 不足25%或運行超時爲止,慢模式下超時時間爲25毫秒

c.如果之前回收鍵邏輯超時,則在Redis觸發內部事件之 前再次以快模式運行回收過期鍵任務,快模式下超時時間 爲1毫秒且2秒內只能運行1次

d.快慢兩種模式內部刪除邏輯相同,執行的超時時間不同

內存溢出控制策略: 具體策略由maxmemory-policy參數控制,redis支持6種策略

noeviction:默認策略,不刪除任何數據,拒絕所有寫入操作並返回客戶端錯誤信息,此時redis只響應讀操作

volatile-lru:根據LRU算法刪除設置了超時屬性(expire)的鍵,直到騰出足夠空間爲止,若無可刪除的鍵,回退到noeviction策略

allkeys-lru:根據LRU算法刪除鍵,不管數據有沒設置超時屬性,直到騰出足夠空間爲止

allkeys-random:隨機刪除所有鍵,直到騰出足夠空間爲止

volatile-random:隨機刪除過期鍵,直到騰出足夠空間爲止

volatile-ttl:根據鍵值對象的ttl屬性,刪除最近將要過期的數據,若沒有回退到noeviction策略

內存溢出控制策略可採用config set maxmemory-policy{policy}動態配置

3.內存優化:

清除長時間未被訪問的key:定期執行scan+object idletime key 命令找出長時間不訪問的key並清除

縮減鍵值對象:降低redis內存最直接的方式,縮減key和value的長度,序列化存儲或壓縮後存入redis,注意時間與空間的平衡

字符串預分配機制(sds):

a.第一次創建len屬性等於數據實際大小,free等於0,不做預分配

b.修改後如果已有free空間不夠且數據小於1M,每次預分配一倍容量,如原有len=60byte,free=0,再追加60byte,預分配120byte,總佔用空間:60byte+60byte+120byte+1byte

c.修改後如果已有free空間不夠且數據大於1MB,每次預分配1MB數據,如原有len=30MB,free=0,當再追加100byte,預分配1MB,總佔用空間:1MB+100byte+1MB+1byte

因此,應儘量減少字符串頻繁修改操作如append、setrange,改爲直接使用set修改字符串

字符串重構:指不一定把每份數據作爲字符串整體存儲,像json數據可以使用hash結構,使用二級結構可節省內存,同時可以使用hmget、hmset支持字段的部分讀取修改,不用每次整體存取

使用hash控制鍵的數量:對於存儲相同的數據內容利用hash數據結構降低外層鍵的數量,可大量節省內存

a.根據鍵規模在客戶端通過分組映射到一組hash對象中,如存在100萬個鍵,可以映射到1000個hash中,每個hash保存1000個元素

b.hash的field可用於記錄原始key字符串,方便哈希查找

c.hash的value保存原始值對象,確保不要超過hash-max-ziplist-value限制

特點:

a.同樣的數據使用ziplist編碼的hash類型存儲比string類型節約內存,節省內存量隨着value空間的減少越來越明顯

b.hash的ziplist類型比string類型寫入耗時,但隨着value空間的減少,耗時逐漸降低

c.使用hash重構後節省內存量效果非常明顯,特別對於存儲小對象的場景,內存只有不到原來的1/5

注意事項:

a.確保hash使用的是ziplist編碼,忌用hashtable編碼

b.hash長度需要控制在1000以內

c.只存儲小對象,大對象醜拒

d.預估鍵的規模

e. 調整hash-max-ziplist-entries和hash-maxziplist-value參數

關於hash鍵與field鍵的設計:

a.當鍵離散度較高時,,可以按字符串位截取,把後三位作爲哈希的field,之前部分作爲哈希的鍵,如:key=1948480哈希key=group:hash:1948,哈希field=480

b.當鍵離散度較低時,可以使用哈希算法打散鍵,如:使用 crc32(key)&10000函數把所有的鍵映射到”0-9999”整數範圍內,哈希field存儲鍵的原始值

c.儘量減少hash鍵和field的長度,如使用部分鍵內容

hash分組控制鍵規模可能帶來的問題:

a.客戶端需要預估鍵的規模並設計hash分組規則,加重客戶端開發成本

b.hash重構後所有的鍵無法再使用超時(expire)和Llru淘汰機制自動刪除,需要手動維護刪除

ps:使用ziplist+hash優化keys後,如果想使用超時刪除功能,可以存儲每個對象寫入的時間,再通過定時任務使用hscan命令掃描數據,找出hash內超時的數據項刪除即可

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章