一次redis主從切換導致的數據丟失與陷入只讀狀態故障

来源:https://www.cnblogs.com/AcAc-t/archive/2023/05/22/redis_master_switch_failure.html
-Advertisement-
Play Games

上一講我們安裝 etcd 服務端,這一講我們來一起學學如何使用 etcd 客戶端常見的命令。文章內容來源於參考資料,如若侵權,請聯繫刪除,謝謝。 > etcd可通過客戶端命令行工具 etcdctl 對etcd進行請求操作 ```sh # 幫助命令,會列出所有的命令和選項,在記不太清命令的時候,可以使 ...


背景

最近一組業務redis數據不斷增長需要擴容記憶體,而擴容記憶體則需要重啟雲主機,在按計劃擴容升級執行主從切換時意外發生了數據丟失與master進入只讀狀態的故障,這裡記錄分享一下。

業務redis高可用架構

該組業務redis使用的是一主一從,通過sentinel集群實現故障時的自動主從切換,這套架構已經平穩運行數年,經歷住了多次實戰的考驗。
高可用架構大體如下圖所示:
image
簡單說一下sentinel實現高可用的原理:
集群的多個(2n+1,N>1)哨兵會定期輪詢redis的所有master/slave節點,如果sentinel集群中超過一半的哨兵判定redis某個節點已經主觀下線,就會將其判定為客觀下線進行相應處理:

  1. 如果下線節點是master,選定一個正常work的slave將其選定為新的master節點。
  2. 如果下線節點是slave,將其從slave節點中移除。

如果已經被客觀下線的節點恢復了正常,sentinel中超過一半哨兵確認後則將其加回可用的slave節點。
所有需要讀寫redis的server並不需要直接寫死redis 主從配置,而是通過訪問sentinel獲取當前redis的主從可用狀態,具體實現方式可以定時查詢sentinel詢問更新,也可以通過訂閱機制讓sentinel在主從變動時主動通知訂閱方更新。
sentinel實現高可用的詳細原理這裡不做過多贅述,有興趣的小伙伴可以移步參考文獻中的相關資料。

具體記憶體擴容流程

sentinel可以在檢測到故障時自動切換redis主從,也可以主動執行sentinel failover mastername 命令實現手動切換主從,所以這次的記憶體擴容重啟流程設計如下(A代表初始master所在雲主機,B代表初始slave所在雲主機):

  1. 升級主機B記憶體配置,重啟主機B
  2. 檢查B重啟後其上的redis slave是否重新同步master數據完成,包括:
    2.1 查看slave redis log是否異常,無異常pass
    2.2 使用info keyspace命令check master、slave 各db key數量是否一致,無異常pass
    2.3 在master寫入一個測試key,在slave上check是否同步成功
    2.4 觀察依賴server log是否有異常
  3. 使用sentinel failover mastername命令手動主從切換,主機A變成新slave,主機B變成新master,根據以前手動切換的經驗走到這一步基本上就穩了--因為這裡本質上和一次普通主從切換已經沒有區別了。
  4. 升級主機A記憶體配置,重啟主機A,執行以下check:
    4.1 查看新slave redis log是否異常
    4.2 使用info keyspace命令check 新master、新slave 各db key數量是否一致,無異常pass
    4.3 在新master寫入測試key,在新slave上check是否同步成功
    4.4 觀察依賴server log是否有異常

至此,若以上步驟都正常通過,一個完美的redis記憶體升級工作就完成了。

主從切換後數據丟失

結果正是沒有想過可能會出問題的步驟3反而出現了問題,直接導致了主從切換後丟掉了部分數據,並且新master進入只讀狀態將近十分鐘。
當時的情況是這樣的:
在執行完步驟3後,check 新slave redis log無異常,正在考慮觀察一會兒後執行主機A的升級重啟操作,api的分鐘級別異常監控觸發了一小波redis相關報警。第一反應在新master與新slave上執行了info keyspace查看key數量是否已經不一致,結果發現master/slave的key數量是一致的--但是再仔細一看:和切換前的key總數百萬級相比切換後key總數降到了十萬級--大部分key數據被丟失了。
查看新master、新slave log都沒有發現明顯log可以解釋為什麼主從切換後會丟失一大半數據這一現象,這時小伙伴第一次提到了是不是記憶體不夠了,當時自己略一思考馬上回覆到:新master剛升級了記憶體,不可能內容擴大後反而記憶體不足的,所以應該不是這個問題。
n分鐘後...
小伙伴再一次提出了是不是maxmemory問題,這一下子點中了關鍵點,馬上想到主機B升級了記憶體是不會有系統層面記憶體不足的問題,但是redis的記憶體使用實際上還會受到maxmemory參數限制,馬上在新master上執行config get maxmemory, 只有3GB,而升級前數據實際使用記憶體超過了6GB!
立刻調大了新master的maxmemory參數,redis很快恢復了可讀寫正常狀態,一大波redis只讀引發的告警通知開始快速下降。

原因定位

緊張又刺激的故障處理就這麼過去了,在優先處理完丟失key數據恢復工作之後,開始回顧整理故障的詳細原因,總共有如下幾個疑問:

  1. 明確記得上個月給主機A、B上的redis都通過config set maxmemory設置為了7GB,為什麼出現問題時查詢B上redis 的maxmemory配置卻變成了3GB?
  2. 如果主機B的maxmemory是3GB,其作為slave時為什麼從master同步超過6GB的數據時不會有問題?--在主從切換前無論是查看info keyspace還是在master上寫入測試key同步check都是OK的。
  3. 為什麼主從切換後主機B上的key數據會丟失?這個是因為maxmemory設置過小,是故障的直接原因。
  4. 為什麼新master由於maxmemory參數超限進入只讀狀態且刪除部分數據後,新master中實際數據占用的大小依然超過>3GB?

如上四個疑問除了問題3已經明確了,剩下三個問題都讓人疑惑--事出詭異必有妖,經過一番探尋得出其答案:

  1. 上個月修改redis maxmemory時,只通過config set命令修改了其運行時配置,而沒有修改對應配置redis.conf上maxmemory的值,主機B上redis在重啟後就會從redis.conf上傳入該maxmemory,該配置正是3GB,同時maxmemory參數是redis節點獨立的配置,slave並不會從master同步該值。
  2. 在redis5.0版本之後,redis引入了一個新的參數replica-ignore-maxmemory,其官方文檔定義如下:
Maxmemory on replicas
By default, a replica will ignore maxmemory (unless it is promoted to master after a failover or manually). It means that the eviction of keys will be handled by the master, sending the DEL commands to the replica as keys evict in the master side.
This behavior ensures that masters and replicas stay consistent, which is usually what you want. However, if your replica is writable, or you want the replica to have a different memory setting, and you are sure all the writes performed to the replica are idempotent, then you may change this default (but be sure to understand what you are doing).
Note that since the replica by default does not evict, it may end up using more memory than what is set via maxmemory (since there are certain buffers that may be larger on the replica, or data structures may sometimes take more memory and so forth). Make sure you monitor your replicas, and make sure they have enough memory to never hit a real out-of-memory condition before the master hits the configured maxmemory setting.
To change this behavior, you can allow a replica to not ignore the maxmemory. The configuration directives to use is:
replica-ignore-maxmemory no

大意是redis作為slave時預設會無視maxmemory參數,這樣可以保證主從的數據始終保持一致。當master/slave實際數據大小均小於其maxmemory設置時,這個參數沒有任何影響,而這次丟失數據的原因正是因為主機B重啟後作為slave時maxmemory(3GB)小於實際數據大小(6GB+),此時replica-ignore-maxmemory 預設開啟保證作為slave時直接無視maxmemory的限制,而當執行sentinel failover mastername將主機B切換為新master後,新master不會受replica-ignore-maxmemory影響,發現自身maxmemory<實際數據大小後直接開始主動淘汰key,從而導致了數據丟失。
4. 至於主機B作為master執行淘汰key策略並最終進入只讀狀態後,其實際數據大小依然>3GB的原因,則是由於線上redis配置的策略是volatile-lru策略,該策略只會淘汰有過期時間的key,對於不過期的key是不淘汰的。

總結

總的來看這次故障的根本原因還是個人對於redis的配置、操作經驗不足,如果在調整運行時maxmemory時能做到以下二者之一,這次故障就不會出現了:

  1. 調整運行時maxmemory時同時調整配置文件maxmemory保持一致。
  2. 將配置文件maxmemory設置為0--表示不限制記憶體使用。

正是因為對redis的認識和經驗不足,沒有想過到運行時配置與靜態配置不一致可能導致的問題,這次不可避免的踩坑了。
但是,作為一個本職RD,半路接手基本靠自學的兼職運維,要考慮到maxmemory的運行配置與靜態配置一致性問題好像也確實不是那麼的理所當然

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ## 創建阻塞的伺服器 當 `ServerSocketChannel` 與 `SockelChannel` 採用預設的阻塞模式時,為了同時處理多個客戶的連接,必須使用多線程 ```java public class EchoServer { private int port = 8000; priv ...
  • 🥑本篇為學習博客園大佬聖殿騎士的《WPF基礎到企業應用系列》以及部分DotNet菜園的《WPF入門教程系列》所作筆記,對應聖殿騎士《WPF基礎到企業應用系列》第 1 - 6 章之間內容,包括 WPF 項目結構、程式的啟動和關閉、程式的生命周期、繼承關係以及常見的佈局控制項及其應用。文章有問題的話歡迎... ...
  • ### 前言 首先拋出一個問題,在XAF項目中,我們現在可不可以選擇EFCore?每個人可能都有自己的答案,這也沒有什麼標準答案。下麵是我的個人看法,在剛接觸XAF時,如何選擇ORM,我也是猶豫了許久,最終選擇了XPO,主要基於以下幾點考慮 1.XPO是DEV的產品,支持力度及傾向性要比EFCore ...
  • 什麼是行為機 顧名思義,類比狀態機每個節點是一個狀態,行為機每個節點是描述一種行為。行為機每個節點之間是互斥的,並且節點相互之間完全不用關心是怎麼切換的。這裡就不講狀態機跟行為樹是怎麼做ai的了,這裡只講用行為機怎麼做一個ai。舉個例子 mmo中的小怪策劃案,大致會這麼寫: 小怪在出生點周圍巡邏。發 ...
  • AI框架 1. 幾種AI的設計 AI在游戲中很多,但是為什麼大家總是感覺ai編寫起來十分困難,我後來思考了一番,主要原因是使用的方法不當。之前大家編寫ai主要有幾種方案: a. 狀態機 我是不知道誰想出來這個做法的,真是無力吐槽。本來對象身上任何數據都是狀態,這種方法又要把一些狀態定義成一種新的節點 ...
  • >本文時間 2023-05-20 >作者:sugerqube漆瓷 `cd`,`vi`,`clear`這些屬於常見常用命令本文不再贅述。 # 安裝命令 `yum install vim`舉例安裝vim `rpm -ivh a.rpm b.rpm c.rpm`舉例安裝a,b,c(涉及包相互依賴) # 用 ...
  • 網上的教學視頻大部分全是以centos為教材底子——沒辦法更換系統了,這樣方便麻! 我參考的文章: https://blog.csdn.net/shengjie87/article/details/106805105#:~:text=%E7%94%A8%E6%8C%82%E8%BD%BD%E5%85 ...
  • Wayland 配置起來確實相對麻煩很多,需要註意很多細節,如果不註意就會出現問題,在這裡說一下可能的現象與解決方法。 根據觀察,這些現象在 GNOME 與 KDE 桌面環境鐘均會出現。 ## 現象 ### App 打開慢 現象為當首次打開一個圖形化的 App 時,需要等待2-3秒鐘才會打開,但是如 ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...