Redis 數據 “丟失” 問題

版權聲明: https://blog.csdn.net/u012322399/article/details/80743173

Redis大部分應用場景是純緩存服務,請求後端有Primary Storage的組件,如MySQL,HBase;請求Redis的鍵未命中,會從primary Storage中獲取數據返回,同時更新Redis緩存。
如果少量數據丟失,相當於請求”緩衝未命中“; 一般對業務的影響是無感知的。
但現在Redis用作存儲的業務場景變多,數據丟失對業務是致命的影響。
本文簡單討論Redis常見數據”丟失“現象,以及怎麼規避;會列舉幾個生產中有意思的情節。

記1次Redis”數據丟失“的故障排查

Redis數據被丟失問題,發生次數也很多; 如何快速定位問題和避免呢。
先分享一個小故事(大家都喜歡帶情節的片,不對是技術文章)

情節:我的Redis掉了90000多個Keys, 是不是DBA有刪除操作?

(時間:12-04日;故事人物:RD(研發工程師)和DBA; 故事:Redis一夜之間不見90000個key)

RD:我們Redis集羣中,以“t_list”前綴的90000多key今早發現都掉了,其他key都在,是不是DBA有清理操作啊?
DBA:沒有維護性操作(一臉懵B和無辜),先止損,把Key從Primary store中導入Redis;我先分析一下原因,有結果了通知你;定位問題前,你也關注一下,避免二次發生。
RD:“已從MySQL把key導入到Redis. 好的,等你消息。”
然後RD就下樓了,DBA扣上他的25元的boss耳機,開始自言自語Troubleshooting.
“這部分key未設置TTL, 查看監控的 expired_keys基本都是0”
“是否達到了maxmeory,key被強制驅逐淘汰了? 查看監控 used_memory_pct未到100%,查看 evicted_keys一直爲0,最近24小時無key被淘汰”
“只是部分key丟失,而且都是同一個key前綴,說明這個兇手很瞭解業務;查看監控的實例總key數, keys指標,發現果斷keys果斷下降,但未變爲0,排除Flushall/flushdb和Redis, 定位是程序或人爲操作”
“如果程序主動刪除key, 就只能是DEL操作,查看監控comdstat_del指標,表示每秒執行的Del次數,果然平時基本爲0,昨晚22:01開始有每秒幾十個的del”
“再查看slowlog, 22:01時,執行 ‘ KEYS tlist*’ 的命令,獲取這類key,進行批量清理”
這時問題定位了, 一首歌多的時間。 然後DBA通知RD排查的結論,讓其他排查程序或人爲操作;

分析證據簡述如下:
從03日的Redis key監控可見,22:00到22:40這個數據key個數下降30000(1個分片,此集羣共3個分片)
redis_keys
03日22:00~22:40分之間,Redis的DEL操作大約12個,持續40min; 刪除126040約29000個key.(3個分片,共刪除約90000個key)
redis_del
查看slowlog監控,2015-12-03 22:01:01 時間點,執行KEYS “tlist*” 獲取所有key的前綴, 目的應該是執行後面的DEL操作
redis_keys

說明:精細化的監控告警很重要。

數據丟失的影響

  • Redis存儲的應用場景,數據丟失是不能接受的;
    因爲Redis的持久化特性,數據還原很難保證一致性,因rdb全備和aof重寫備份,RPO不能像MySQL這樣保證恢復到故障操作的前一個事務。
  • 緩存的應用場景,如果大量緩存數據丟失,往往導致後端存儲組件”打死“,應用程序雪崩的情況

常見Redis數據丟失的情況

  • 程序bug或人爲誤操作
  • 因客戶端緩衝區內存使用過大,導致大量鍵被LRU淘汰
  • 主庫故障後自動重啓,可能導致數據丟失
  • 網絡分區的問題,可能導致短時間的寫入數據丟失
  • 主從複製數據不一致,發生故障切換後,出現數據丟失
  • 大量過期鍵,同時被淘汰清理

程序bug或人爲誤操作

如前文情節1,程序bug誤刪除數據; DBA/RD誤操作執行flushall/flushdb這類命令。
這類問題的預防和監控
1 重命名危險命令:keys(程度大批量誤刪除,很多通過keys獲取鍵後再刪除),flushall,flushdb
2 細化幾個重要的監控項:

  • 實例當前的鍵個數(dbsize/info), 當大量鍵丟失時,可通過此項歷史監控圖,定位發生的時間範圍
  • 各類刪除命令的執行數監控:cmdtats_flushall, cmdstats_flushdb,cmdstat_del
    對應時間範圍,確認具體是什麼操作

因客戶端緩衝區內存使用過大,導致大量鍵被LRU淘汰

因客戶端緩衝區的內存大小很難限制,它們消耗的內存數會計算在used_memory內;如果使用不當,
導致緩衝區內存使用過大,達到maxmemory限制;(緩存場景)會導致大量的鍵被淘汰,最壞會把所有鍵清理,緩衝無鍵可淘汰,寫入失敗。相當於整個緩衝失效,對業務影響較大。

關於Redis客戶端緩衝區問題,詳細分析見之前文章Redis Clients Two Buffers

這類問題的預防和監控:
1 業務容量規劃時把緩衝正常消耗計算在內,合理高大maxmemory的限制;
每個實例最好可預留幾百M(大小根據客戶端連接數和key的使用有關,根據大小集羣合理調整)
2 對輸出緩衝區設置合理limit;如normal設置10MB, SLAVE設置1GB等。 如果複製因slave線程輸出緩衝區反覆同步,需臨時調大slave client-output-buffer,要同時調大maxmemory限制。

說明:關於Redis複製中斷和無限同步,詳細分析請見Redis複製中斷和無限同步問題

3 主要監控

  • 監控內存使用大小 used_memory
  • 監控兩個buffer的使用量client_longest_output_list和client_biggest_input_buf
  • 監控鍵的LRU驅逐數量:evicted_keys

主庫故障後自動重啓,可能導致數據全部丟失

這種故障發生,極有可能數據全部丟失。
問題發生的現象:時間點T1,主庫故障關閉了,因設置有自動重啓的守護程序,時間點T2主庫被重新拉起,因(T2-T1)時間間隔過小,未達到Redis集羣或哨兵的主從切換判斷時長;這樣從庫發現主庫runid變了或斷開過,會全量同步主庫rdb清理,並清理自己的數據。
而爲保障性能,Redis主庫往往不做數據持久化設置,那麼時間點T2啓動的主庫,很有可能是個空實例(或很久前的rdb文件)。

這種問題發生時間間隔,一般小於1分鐘,可能監控告警無法感知到。
這類總是的預防和監控:
1 強烈反對Redis粗暴地設置自動重啓
2 這種監控鍵個數的變化,緩存命中率,同時ELK類型準實時監控redis日誌變化並告警

建議:數據庫這類重“狀態性”服務,不建議程序暴力自動重啓

網絡分區的問題,可能導致短時間的寫入數據丟失

這種問題出現丟失數據都很少,網絡分區時,Redis集羣或哨兵在判斷故障切換的時間窗口,這段時間寫入到原主庫的數據,5秒~15秒的寫入量。
詳細分析參考:
Reply to Aphyr attack to Sentinel
redis-sentinel-at-flickr

圖片(引至參考2): Redis哨兵結構的網絡分區導致的“split-brain”場景
redis_keys

主從複製數據不一致,發生故障切換後,出現數據丟失

主從數據出現不一致,發生故障切換,從庫提升爲主後,導致數據丟失的情況。
關於Redis複製數據不一致,請參考Redis複製主從數據不-致

大量過期鍵,同時被淘汰清理

這類情況不是真正的“數據丟失”,只是定期主動清理Redis堆積的過期鍵,會導致Redis的鍵個數(dbsize)出現陡降(最大能達20%)。業務方常誤以爲有數據丟失。

這時可通過監控過期鍵淘汰的數量:expireed_keys的增長量,與dbsize鍵總數減少數據量是否相等。

說明:關於過期鍵,大量堆積成爲“死鍵”問題,詳細分析參考Redis的“死鍵”問題

歡迎大家留言補充,遇到的數據丟失場景。

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