那一天,我被Redis主從架構支配的恐懼

Java3y發表於2021-11-18

面試官:要不你來講講你最近在看的點唄?可以拉出來一起討論下(今天我也不知道要問什麼)

候選者:最近在看「Redis」相關的內容

面試官:嗯,我記得已經問過Redis的基礎和持久化了

面試官要不你來講講你公司的Redis是什麼架構的咯?

候選者:我前公司的Redis架構是「分片叢集」,使用的是「Proxy」層來對Key進行分流到不同的Redis伺服器上

候選者:支援動態擴容、故障恢復等等...

面試官:那你來聊下Proxy層的架構和基本實現原理?

候選者:抱歉,這塊由中介軟體團隊負責,具體我也沒仔細看過

候選者:...

面試官:....

候選者:不過,我可以給你講講現有常見開源的Redis架構(:

面試官:那隻能這樣了,好吧,你開始吧

候選者:那我從基礎講起吧?

候選者:在之前提到了Redis有持久化機制,即便Redis重啟了,可以依靠RDB或者AOF檔案對資料進行重新載入

候選者:但在這時,只有一臺Redis伺服器儲存著所有的資料,此時如果Redis伺服器「暫時」沒辦法修復了,那依賴Redis的服務就沒了

候選者:所以,為了Redis「高可用」,現在基本都會給Redis做「備份」:多啟一臺Redis伺服器,形成「主從架構」

候選者:「從伺服器」的資料由「主伺服器」複製過去,主從伺服器的資料是一致的

候選者:如果主伺服器掛了,那可以「手動」把「從伺服器」升級為「主伺服器」,縮短不可用時間

面試官那「主伺服器」是如何把自身的資料「複製」給「從伺服器」的呢?

候選者:「複製」也叫「同步」,在Redis使用的是「PSYNC」命令進行同步,該命令有兩種模型:完全重同步和部分重同步

候選者:可以簡單理解為:如果是第一次「同步」,從伺服器沒有複製過任何的主伺服器,或者從伺服器要複製的主伺服器跟上次複製的主伺服器不一樣,那就會採用「完全重同步」模式進行復制

候選者:如果只是由於網路中斷,只是「短時間」斷連,那就會採用「部分重同步」模式進行復制

候選者:(假如主從伺服器的資料差距實在是過大了,還是會採用「完全重同步」模式進行復制)

面試官那「同步」的原理過程可以稍微講下嘛?

候選者:嗯,沒問題的

候選者:主伺服器要複製資料到從伺服器,首先是建立Socket「連線」,這個過程會幹一些資訊校驗啊、身份校驗啊等事情

候選者:然後從伺服器就會發「PSYNC」命令給主伺服器,要求同步(這時會帶「伺服器ID」RUNID和「複製進度」offset引數,如果從伺服器是新的,那就沒有)

候選者:主伺服器發現這是一個新的從伺服器(因為引數沒帶上來),就會採用「完全重同步」模式,並把「伺服器ID」(runId)和「複製進度」(offset)發給從伺服器,從伺服器就會記下這些資訊。

面試官:嗯...

候選者:隨後,主伺服器會在後臺生成RDB檔案,通過前面建立好的連線發給從伺服器

候選者:從伺服器收到RDB檔案後,首先把自己的資料清空,然後對RDB檔案進行載入恢復

候選者:這個過程中,主伺服器也沒閒著(繼續接收著客戶端的請求)

面試官:嗯...

候選者:主伺服器把生成RDB檔案「之後修改的命令」會用「buffer」記錄下來,等到從伺服器載入完RDB之後,主伺服器會把「buffer」記錄下的命令都發給從伺服器

候選者:這樣一來,主從伺服器就達到了資料一致性了(複製過程是非同步的,所以資料是『最終一致性』)

面試官:嗯...

面試官那「部分重同步」的過程呢?

候選者:嗯,其實就是靠「offset」來進行部分重同步。每次主伺服器傳播命令的時候,都會把「offset」給到從伺服器

候選者:主伺服器和從伺服器都會將「offset」儲存起來(如果兩邊的offset存在差異,那麼說明主從伺服器資料未完全同步)

候選者:從伺服器斷連之後,就會發「PSYNC」命令給主伺服器,同樣也會帶著RUNID和offset(重連之後,這些資訊還是存在的)

面試官:嗯...

候選者:主伺服器收到命令之後,看RUNID是否能對得上,對得上,說明這可能以前就複製過一部分了

候選者:接著檢查該「offset」是否在主伺服器記錄的offset還存在

候選者:(這裡解釋下,因為主伺服器記錄offset使用的是一個環形buffer,如果該buffer滿了,會覆蓋以前的記錄)

候選者:如果找到了,那就把從缺失的一部分offer開始,把對應的修改命令發給從伺服器

候選者:如果從環形buffer沒找到,那隻能使用「完全重同步」模式再次進行主從複製了

面試官主從複製這塊我瞭解了,那你說到現在,Redis主庫如果掛了,你還是得「手動」將從庫升級為主庫啊

面試官你知道有什麼辦法能做到「自動」進行故障恢復嗎?

候選者:必須的啊,接下來就到了「哨兵」登場了

面試官:開始你的表演吧。

候選者:「哨兵」乾的事情主要就是:監控(監控主伺服器的狀態)、選主(主伺服器掛了,在從伺服器選出一個作為主伺服器)、通知(故障傳送訊息給管理員)和配置(作為配置中心,提供當前主伺服器的資訊)

候選者:可以把「哨兵」當做是執行在「特殊」模式下的Redis伺服器,為了「高可用」,哨兵也是叢集架構的。

候選者:首先它需要跟Redis主從伺服器建立對應的連線(獲取它們的資訊)

候選者:每個哨兵不斷地用ping命令看主伺服器有沒有下線,如果主伺服器在「配置時間」內沒有正常響應,那當前哨兵就「主觀」認為該主伺服器下線了

候選者:其他「哨兵」同樣也會ping該主伺服器,如果「足夠多」(還是看配置)的哨兵認為該主伺服器已經下線,那就認為「客觀下線」,這時就要對主伺服器執行故障轉移操作。

面試官:嗯...

候選者:「哨兵」之間會選出一個「領頭」,選出領頭的規則也比較多,總的來說就是先到先得(哪個快,就選哪個)

候選者:由「領頭哨兵」對已下線的主伺服器進行故障轉移

面試官:嗯...

候選者:首先要在「從伺服器」上挑選出一個,來作為主伺服器

候選者:(這裡也挑選講究,比如:從庫的配置優先順序、要判斷哪個從伺服器的複製offset最大、RunID大小、跟master斷開連線的時長...)

候選者:然後,以前的從伺服器都需要跟新的主伺服器進行「主從複製」

候選者:已經下線的主伺服器,再次重連的時候,需要讓他成為新的主伺服器的從伺服器

面試官嗯...我想問問,Redis在主從複製的和故障轉移的過程中會導致資料丟失嗎

候選者:顯然是會的,從上面的「主從複製」流程來看,這個過程是非同步的(在複製的過程中:主伺服器會一直接收請求,然後把修改命令發給從伺服器)

候選者:假如主伺服器的命令還沒發完給從伺服器,自己就掛掉了。這時候想要讓從伺服器頂上主伺服器,但從伺服器的資料是不全的(:

候選者:還有另一種情況就是:有可能哨兵認為主伺服器掛了,但真實是主伺服器並沒有掛( 網路抖動),而哨兵已經選舉了一臺從伺服器當做是主伺服器了,此時「客戶端」還沒反應過來,還繼續寫向舊主伺服器寫資料

候選者:等到舊主伺服器重連的時候,已經被納入到新主伺服器的從伺服器了...所以,那段時間裡,客戶端寫進舊主伺服器的資料就丟了

候選者:上面這兩種情況(主從複製延遲&&腦裂),都可以通過配置來「儘可能」避免資料的丟失

候選者:(達到一定的閾值,直接禁止主伺服器接收寫請求,企圖減少資料丟失的風險)

面試官要不再來聊聊Redis分片叢集?

候選者:嗯...分片叢集說白了就是往每個Redis伺服器儲存一部分資料,所有的Redis伺服器資料加起來,才組成完整的資料(分散式)

候選者:要想組成分片叢集,那就需要對不同的key進行「路由」(分片)

候選者:現在一般的路由方案有兩種:「客戶端路由」(SDK)和「服務端路由」(Proxy)

候選者:客戶端路由的代表(Redis Cluster),服務端路由的代表(Codis)

面試官要不來詳細講講它們的區別唄?

候選者:今天有點兒困了,要不下次唄?

本文總結

  • Redis實現高可用

    • AOF/RDB持久化機制
    • 主從架構(主伺服器掛了,手動由從伺服器頂上)
    • 引入哨兵機制自動故障轉義
  • 主從複製原理

    • PSYNC命令兩種模式:完全重同步、部分重同步
    • 完全重同步:主從伺服器建立連線、主伺服器生成RDB檔案發給從伺服器、主伺服器不阻塞(相關修改命令記錄至buffer)、將修改命令發給從伺服器
    • 部分重同步:從伺服器斷線重連,傳送RunId和offset給主伺服器,主伺服器判斷offset和runId,將還未同步給從伺服器的offset相關指令進行傳送
  • 哨兵機制

    • 哨兵可以理解為特殊的Redis伺服器,一般會組成哨兵叢集
    • 哨兵主要工作是監控、告警、配置以及選主
    • 當主伺服器發生故障時,會「選出」一臺從伺服器來頂上「客觀下線」的伺服器,由「領頭哨兵」進行切換
  • 資料丟失

    • Redis的主從複製和故障轉移階段都有可能發生資料丟失問題(通過配置儘可能避免)

歡迎關注我的微信公眾號【Java3y】來聊聊Java面試,對線面試官系列持續更新中!

那一天,我被Redis主從架構支配的恐懼

【對線面試官-移動端】系列 一週兩篇持續更新中!

【對線面試官-電腦端】系列 一週兩篇持續更新中!

原創不易!!求三連!!

相關文章