redis存在大量臟頁問題的追查記錄


from:https://www.zybuluo.com/SailorXiao/note/136014 

case現場

線上發現一台機器內存負載很重,top后發現一個redis進程占了大量的內存,TOP內容如下:

27190   root    20   0  18.6g   18g  600 S  0.3     59.2    926:17.83   redis-server  

發現redis占了18.6G的物理內存。由於redis只是用於cache一些程序數據,覺得很不可思議,執行redis的info命令,發現實際數據占用只有112M,如下:

# Memory used_memory:118140384 used_memory_human:112.67M used_memory_rss:19903766528 used_memory_peak:17871578336 used_memory_peak_human:16.64G used_memory_lua:31744 mem_fragmentation_ratio:168.48 mem_allocator:libc 

於是用了pmap -x 27190 查看redis進程的內存映像信息,結果如下:

27190:   ./redis-server ../redis.conf Address             Kbytes      RSS     Dirty           Mode        Mapping 0000000000400000    548         184         0           r-x--       redis-server 0000000000689000    16          16          16          rw---       redis-server 000000000068d000    80          80          80          rw---       [ anon ] 0000000001eb6000    132         132         132         rw---       [ anon ] 0000000001ed7000    19436648    19435752    19435752    rw---       [ anon ] 00007f5862cb2000    4           0           0           -----       [ anon ] 

發現存在大量的內存臟頁。現在內存負載高的問題已經比較清晰了,是由於redis的臟頁占用了大量的內存引起的。可是,redis為什么會存在那么多的臟頁呢?

case 分析

看了下linux臟頁的定義:

臟頁是linux內核中的概念,因為硬盤的讀寫速度遠趕不上內存的速度,系統就把讀寫比較頻繁的數據事先放到內存中,以提高讀寫速度,這就叫高速緩存,linux是以頁作為高速緩存的單位,當進程修改了高速緩存里的數據時,該頁就被內核標記為臟頁,內核將會在合適的時間把臟頁的數據寫到磁盤中去,以保持高速緩存中的數據和磁盤中的數據是一致的。 

也就是說,臟頁是因為內存中的很多數據沒來得及更新到磁盤導致的。看了下linux系統的臟頁flush機制: 
http://blog.chinaunix.net/uid-17196076-id-2817733.html 
發現跟內存flush的可以進行設置(/proc/sys/vm底下)

dirty_background_bytes/dirty_background_ratio:     - 當系統的臟頁到達一定值(bytes或者比例)后,啟動后台進程把臟頁刷到磁盤中,此時不影響內存的讀寫(當bytes設置存在時,ratio是自動計算的)  dirty_bytes/dirty_ratio:     - 當系統的臟頁到達一定值(bytes或者比例)后,啟動進程把臟頁刷到磁盤中,此時內存的寫可能會被阻塞(當bytes設置存在時,ratio是自動計算的)  dirty_expire_centisecs:     - 當內存和磁盤中的數據不一致存在多久以后(單位為百分之一秒),才會被定義為dirty,並在下一次的flush中被清理。不一致以磁盤中文件的inode時間戳為准  dirty_writeback_centisecs:     - 系統每隔一段時間,會把dirty flush到磁盤中(單位為百分之一秒) 

查看當前系統的flush配置,發現沒問題,dirty_background_ratio為10%,dirty_ratio為20%,dirty_writeback_centisecs為5s,dirty_expire_centisecs為30s,可是為啥redis的臟頁沒有被flush到磁盤中呢?

一般臟頁是要把內存中的數據flush到磁盤中,那么會不會是redis的持久化導致了臟頁呢?查看下redis關於這些方面的配置:

rdb持久化已經被關閉 # save 900 1 # save 300 10 # save 60 10000  # append持久化也被關閉 appendonly no  # 最大內存設置、內存替換策略都是默認值 # maxmemory <bytes> # maxmemory-policy volatile-lru 

如上所示,發現redis自身已經完全關閉持久化,只是作為cache使用,而且對於最大內存使用默認值(代表沒有限制),內存的淘汰機制是volatile-lru。翻看redis的文檔,查看對應的淘汰機制:

volatile-lru:      從已設置過期時間的數據集(server.db[i].expires)中挑選最近最少使用的數據淘汰(默認值) volatile-ttl:      從已設置過期時間的數據集(server.db[i].expires)中挑選將要過期的數據淘汰 volatile-random:   從已設置過期時間的數據集(server.db[i].expires)中任意選擇數據淘汰 allkeys-lru:       從數據集(server.db[i].dict)中挑選最近最少使用的數據淘汰 allkeys-random:    從數據集(server.db[i].dict)中任意選擇數據淘汰 no-enviction:      禁止驅逐數據 

而在當前的使用環境中,程序對redis的使用是當做cache,並且會對數據設置expire超時時間,到期后等待redis進行刪除的。那么臟頁的原因,是不是因為過期數據清理機制的問題呢(比如清理不及時等)?因此,需要查看redis在對過期數據進行刪除時采取的策略,參考信息如下: 
Redis中的內存釋放與過期鍵刪除 
redis 過期鍵的清除

redis過期鍵刪除機制:

惰性刪除:     -  expire的鍵到期后,不會自動刪除,不過在每次讀取該鍵時進行檢查,檢查該鍵是否已經過期,如果過期,則進行刪除動作。這樣可以保證刪除操作只會在非做不可的情況下進行      定期刪除:     - 每隔一段時間執行一次cron刪除操作,從每個db的expire-keys中隨機找出一定數量的key(默認是20個),檢查key是否超時。如果已經超時,則進行刪除     - 通過限制刪除操作執行的時長和頻率,並以此來減少刪除操作對 CPU 時間的影響。  大於maxmemory的自動刪除:     - 每次client和server進行command交互時,server都會檢查maxmemory的使用情況     - 如果當前的內存已經超過了max-memory,那么則進行清理,直到內存占用在maxmemory線以下     - 清理的策略基於淘汰機制(LRU,TTL,RANDOM等) 

redis對於過期鍵刪除使用的是 惰性刪除 + 定期刪除 + 大於maxmemory的自動清除 的策略。

case 定位

通過以上的分析,問題已經比較明確了,原因如下:

  1. 由於某種原因,redis使用的內存越來越大(可能是由於惰性刪除,導致expire的數據越積越多,或者其它原因,具體原因取決於redis內部的實現)
  2. redis由於只當做cache,並沒有實際讀寫文件,因此操作系統並不會幫它flush到磁盤中(因為沒有地方可以flush)
  3. 由於redis沒有設置maxmemory,因此默認的是機器的內存大小,只有當redis自身使用的內存達到機器內存大小時,redis才會自身進行清理(volatile-lru機制)
  4. 因此當前的redis的內存越來越大,而且臟頁數據越來越多(大部分可能都是已經過期的數據)

case解決

為了解決這個問題,比較方便且合理的方法就是:

  • 惰性刪除有時候並不是很靠譜,特別對於一些log類型的數據,在寫入redis后就不管了,雖然設置了超時時間,但是沒有效果
  • 定期刪除對於expire-keys也不是很靠譜,存在隨機性,也可能過期很久的數據沒有刪除
  • 相對而言,比較合理的方式是基於使用情況設置redis的maxmemory大小,用於讓redis實現自身的數據清理機制,確保把mem限制在maxmemory設定范圍內


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM