本篇文章不涉及redis的安裝配置,百度或谷歌即可,很簡單。
首先,我來說說redis的應用場景,大部分公司都是將redis作為緩存服務器,或者作為ELK日志收集里面的緩存角色(其他這里就不做介紹,比如作為數據庫、訂閱/發布等)。在這些應用中,我們最值得關注的是redis內存管理機制。下面我就從如下幾個問題,開始探討。
Redis是否需要開啟限制內存大小?
在實際生產當中,設置redis內存大小是必須的。否則redis會無限制使用內存,直到將內存資源消耗殆盡。一般配置內存不超過機器內存3/4。如果超過此值,就需要考慮分片或拆分數據。
Redis查看內存及參數說明
Redis INFO命令說明,通過redis info命令可以知道當前redis資源使用情況。我們這里不涉及其他,只談內存。為了快速定位並解決性能問題,這里選擇幾個關鍵性的數據指標,它包含了大多數人在使用Redis上會經常碰到的性能問題。
used_memory:274704400 used_memory_human:261.98M used_memory_rss:284229632 used_memory_rss_human:271.06M used_memory_peak:274704400 used_memory_peak_human:261.98M used_memory_peak_perc:100.00% used_memory_overhead:80161972 used_memory_lua:37888 used_memory_lua_human:37.00K maxmemory_policy:noeviction
其他字段代表的含義,都以字節為單位:
- used_memory:由redis內存分配器分配的內存總量。
- used_memory_human:以標准的格式返回Redis分配的內存總量。
- used_memory_rss:從操作系統的角度返回Redis已分配的內存總量(俗稱常駐集大小)。這個值和top、ps等命令的輸出一致。
- used_memory_peak:Redis內存消耗峰值(以字節為單位)。
- used_memory_peak_human:以人類可讀的格式返回Redis的內存消耗峰值。
- mem_fragmentation_ratio:內存碎片率。used_memory_rss和used_memory之間的比率。
- used_memory_lua: Lua腳本引擎所使用的內存大小。
- mem_allocator: 在編譯時指定的Redis使用的內存分配器,可以是libc、jemalloc、tcmalloc。
當redis的內存使用超過最大可用內存時,那么操作系統開始進行內存與swap空間交換,把內存中舊的或不再使用的內容寫入硬盤上(硬盤上的這塊空間叫Swap分區),以便騰出新的物理內存給新頁或活動頁(page)使用。但是,這樣會造成redis性能下降。如果出現這種情況,需要及時增加內存。
Redis數據持久化
Redis的持久化數據是通過RDB快照和AOF追加實現,他們實現redis宕機時減少最少數據丟失(不能100%實現數據不丟失)。當開啟並觸發快照功能時,Redis會fork一個子進程把當前內存中的數據完全復制一份寫入到硬盤上。因此若是當前使用內存超過可用內存的45%時觸發快照功能,那么此時進行的內存交換會變的非常危險(可能會丟失數據)。也就是說,redis在save的時候會出現雙倍內存的使用,如果此時不能保證redis有多余可用內存 ,那么倘若在這個時候實例上有大量頻繁的更新操作,問題會變得更加嚴重。
多留一倍內存是最安全的。重寫AOF文件和RDB文件的進程(即使不做持久化,復制到Slave的時候也要寫RDB)會fork出一條新進程來,采用了操作系統的Copy-On-Write策略(如果父進程的內存沒被修改,子進程與父進程共享Page。如果父進程的Page被修改, 會復制一份改動前的內容給新進程),留意Console打出來的報告,如"RDB: 1215 MB of memory used by copy-on-write"。在系統極度繁忙時,如果父進程的所有Page在子進程寫RDB過程中都被修改過了,就需要兩倍內存。不過,在實際生產中,可以通過如下方式來實現減少內存開銷。
- 盡可能使用hash數據結構。
- 設置key的過期時間。
- 回收key。
回收key的策略有幾種,分別如下所示:
- volatile-lru:使用LRU算法從已設置過期時間的數據集合中淘汰數據。
- volatile-ttl:從已設置過期時間的數據集合中挑選即將過期的數據淘汰。
- volatile-random:從已設置過期時間的數據集合中隨機挑選數據淘汰。
- allkeys-lru:使用LRU算法從所有數據集合中淘汰數據。
- allkeys-random:從數據集合中任意選擇數據淘汰
- no-enviction:禁止淘汰數據。
接下來,我們就說說,redis持久化機制:RDB和AOF
RDB: snapshot
二進制格式,按照指定的策略,周期性的將數據保存至磁盤,數據文件默認為dump.db;
客戶端也可顯示SAVE或BGSAVE命令啟動快照保存機制;
SAVE: 同步,在主線程中保存快照,此時會阻塞所有客戶端請求,並且SAVE是完整備份,所以此時需要大量資源(分配的虛擬空間,只有進程數據變動時才會給子進程分配物理內存);
BGSAVE: 異步,客戶端請求不會阻塞,默認使用它進行快照;
AOF: Append Only File
記錄每一次寫操作至指定的文件尾部實現持久化,當redis重啟時,可通過重新執行文件中的命令在內存重建數據庫;
BGREWRITEAOF: AOF文件重寫,不會讀取正在使用AOF文件,而通過將內存中的數據以命令的方式保存到臨時文件中,完成之后替換原來的AOF文件;
AOF追加過程:
- (1) redis 主進程通過fork創建子進程;
- (2) 子進程根據redis內存中的數據創建數據庫重建命令序列於臨時文件;
- (3) 父進程集成client請求,並會把請求中的寫操作追加至原AOF文件。額外地,這些新的寫請求還會被放置於一個緩沖隊列中;
- (4) 子進程重新完成,會通知父進程,父進程把緩沖中的命令寫到文件中;
- (5) 父進程用臨時文件替換原AOF文件;
注意:持久化本身不能取代備份,還需要制定備份策略,對redis數據庫定期進行備份;
Redis緩存命中率
redis info命令提供強大的監控功能,能夠查看當前緩存的數據狀態。主要涉及到的字段如下:
keyspace_hits:14414110 #命中key的次數 keyspace_misses:3228654 #未命中key次數 used_memory:433264648 #redis內存分配器分配內存大小 expired_keys:1333536 #運行以來過期key數量 evicted_keys:1547380 #運行以來刪除key數量
緩存的命中率公式:keyspace_hits / ( keyspace_hits + keyspace_misses ) 。一個緩存失效機制,和過期時間設計良好的系統,命中率可以做到95%以上。
Redis 延遲故障排查
需要使用showlog去查看是否有命令導致延遲。
禁用巨大透明內存頁。( echo never > /sys/kernel/mm/transparent_hugepage/enabled)
啟用並使用Redis的延遲監視器功能,以便獲得對Redis實例中的延遲事件和原因的可讀描述。
由swap引發的延遲。
由AOF和磁盤I/O導致的延遲。
以上參考官方文檔:https://redis.io/topics/latency,每個問題的排查過程、方式都在文檔中有說明。
