面試官問我Redis集群,我真的是


面試官聊下Redis的分片集群,先聊 Redis Cluster好咯?

面試官Redis Cluser是Redis 3.x才有的官方集群方案,這塊你了解多少?

候選者:嗯,要不還是從基礎講起唄?

候選者:在前面聊Redis的時候,提到的Redis都是「單實例」存儲所有的數據。

候選者:1. 主從模式下實現讀寫分離的架構,可以讓多個從服務器承載「讀流量」,但面對「寫流量」時,始終是只有主服務器在抗。

候選者:2. 「縱向擴展」升級Redis服務器硬件能力,但升級至一定程度下,就不划算了。

候選者:縱向擴展意味着「大內存」,Redis持久化時的"成本"會加大(Redis做RDB持久化,是全量的,fork子進程時有可能由於使用內存過大,導致主線程阻塞時間過長)

候選者:所以,「單實例」是有瓶頸的

候選者:「縱向擴展」不行,就「橫向擴展」唄。

候選者:用多個Redis實例來組成一個集群,按照一定的規則把數據「分發」到不同的Redis實例上。當集群所有的Redis實例的數據加起來,那這份數據就是全的

候選者:其實就是「分布式」的概念(:只不過,在Redis里,好像叫「分片集群」的人比較多?

候選者:從前面就得知了,要「分布式存儲」,就肯定避免不了對數據進行「分發」(也是路由的意思)

候選者:從Redis Cluster講起吧,它的「路由」是做在客戶端的(SDK已經集成了路由轉發的功能)

候選者:Redis Cluster對數據的分發的邏輯中,涉及到「哈希槽」(Hash Solt)的概念

候選者:Redis Cluster默認一個集群有16384個哈希槽,這些哈希槽會分配到不同的Redis實例中

候選者:至於怎么「瓜分」,可以直接均分,也可以「手動」設置每個Redis實例的哈希槽,全由我們來決定

候選者:重要的是,我們要把這16384個都得瓜分完,不能有剩余!

候選者:當客戶端有數據進行寫入的時候,首先會對key按照CRC16算法計算出16bit的值(可以理解為就是做hash),然后得到的值對16384進行取模

候選者:取模之后,自然就得到其中一個哈希槽,然后就可以將數據插入到分配至該哈希槽的Redis實例中

面試官那問題就來了,現在客戶端通過hash算法算出了哈希槽的位置,那客戶端怎么知道這個哈希槽在哪台Redis實例上呢?

候選者:是這樣的,在集群的中每個Redis實例都會向其他實例「傳播」自己所負責的哈希槽有哪些。這樣一來,每台Redis實例就可以記錄着「所有哈希槽與實例」的關系了(:

候選者:有了這個映射關系以后,客戶端也會「緩存」一份到自己的本地上,那自然客戶端就知道去哪個Redis實例上操作了

面試官那我又有問題了,在集群里也可以新增或者刪除Redis實例啊,這個怎么整?

候選者:當集群刪除或者新增Redis實例時,那總會有某Redis實例所負責的哈希槽關系會發生變化

候選者:發生變化的信息會通過消息發送至整個集群中,所有的Redis實例都會知道該變化,然后更新自己所保存的映射關系

候選者:但這時候,客戶端其實是不感知的(:

候選者:所以,當客戶端請求時某Key時,還是會請求到「原來」的Redis實例上。而原來的Redis實例會返回「moved」命令,告訴客戶端應該要去新的Redis實例上去請求啦

候選者:客戶端接收到「moved」命令之后,就知道去新的Redis實例請求了,並且更新「緩存哈希槽與實例之間的映射關系」

候選者:總結起來就是:數據遷移完畢后被響應,客戶端會收到「moved」命令,並且會更新本地緩存

面試官那數據還沒完全遷移完呢?

候選者:如果數據還沒完全遷移完,那這時候會返回客戶端「ask」命令。也是讓客戶端去請求新的Redis實例,但客戶端這時候不會更新本地緩存

面試官:了解了

面試官:說白了就是,如果集群Redis實例存在變動,由於Redis實例之間會「通訊」

面試官:所以等到客戶端請求時,Redis實例總會知道客戶端所要請求的數據在哪個Redis實例上

面試官:如果已經遷移完畢了,那就返回「move」命令告訴客戶端應該去找哪個Redis實例要數據,並且客戶端應該更新自己的緩存(映射關系)

面試官:如果正在遷移中,那就返回「ack」命令告訴客戶端應該去找哪個Redis實例要數據

候選者:不愧是你...

面試官那你知道為什么哈希槽是16384個嗎?

候選者:嗯,這個。是這樣的,Redis實例之間「通訊」會相互交換「槽信息」,那如果槽過多(意味着網絡包會變大),網絡包變大,那是不是就意味着會「過度占用」網絡的帶寬

候選者:另外一塊是,Redis作者認為集群在一般情況下是不會超過1000個實例

候選者:那就取了16384個,即可以將數據合理打散至Redis集群中的不同實例,又不會在交換數據時導致帶寬占用過多

面試官:了解了

面試官那你知道為什么對數據進行分區在Redis中用的是「哈希槽」這種方式嗎?而不是一致性哈希算法

候選者:在我理解下,一致性哈希算法就是有個「哈希環」,當客戶端請求時,會對Key進行hash,確定在哈希環上的位置,然后順時針往后找,找到的第一個真實節點

候選者:一致性哈希算法比「傳統固定取模」的好處就是:如果集群中需要新增或刪除某實例,只會影響一小部分的數據

候選者:但如果在集群中新增或者刪除實例,在一致性哈希算法下,就得知道是「哪一部分數據」受到影響了,需要進行對受影響的數據進行遷移

面試官:嗯...

候選者:而哈希槽的方式,我們通過上面已經可以發現:在集群中的每個實例都能拿到槽位相關的信息

候選者:當客戶端對key進行hash運算之后,如果發現請求的實例沒有相關的數據,實例會返回「重定向」命令告訴客戶端應該去哪兒請求

候選者:集群的擴容、縮容都是以「哈希槽」作為基本單位進行操作,總的來說就是「實現」會更加簡單(簡潔,高效,有彈性)。過程大概就是把部分槽進行重新分配,然后遷移槽中的數據即可,不會影響到集群中某個實例的所有數據。

面試官那你了解「服務端 路由」的大致原理嗎?

候選者:嗯,服務端路由一般指的就是,有個代理層專門對接客戶端的請求,然后再轉發到Redis集群進行處理

候選者:上次最后面試的時候,也提到了,現在比較流行的是Codis

候選者:它與Redis Cluster最大的區別就是,Redis Cluster是直連Redis實例的,而Codis則客戶端直連Proxy,再由Proxy進行分發到不同的Redis實例進行處理

候選者:在Codis對Key路由的方案跟Redis Cluster很類似,Codis初始化出1024個哈希槽,然后分配到不同的Redis服務器中

候選者:哈希槽與Redis實例的映射關系由Zookeeper進行存儲和管理,Proxy會通過Codis DashBoard得到最新的映射關系,並緩存在本地上

面試官那如果我要擴容Codis Redis實例的流程是怎么樣的?

候選者:簡單來說就是:把新的Redis實例加入到集群中,然后把部分數據遷移到新的實例上

候選者:大概的過程就是:1.「原實例」某一個Solt的部分數據發送給「目標實例」。2.「目標實例」收到數據后,給「原實例」返回ack。3.「原實例」收到ack之后,在本地刪除掉剛剛給「目標實例」的數據。4.不斷循環1、2、3步驟,直至整個solt遷移完畢

候選者:Codis也是支持「異步遷移」的,針對上面的步驟2,「原實例」發送數據后,不等待「目標實例」返回ack,就繼續接收客戶端的請求。

候選者:未遷移完的數據標記為「只讀」,不會影響到數據的一致性。如果對遷移中的數據存在「寫操作」,那會讓客戶端進行「重試」,最后會寫到「目標實例」上

候選者:還有就是,針對 bigkey,異步遷移采用了「拆分指令」的方式進行遷移,比如有個set元素有10000個,那「原實例」可能就發送10000條命令給「目標實例」,而不是一整個bigkey一次性遷移(因為大對象容易造成阻塞)

面試官:了解了。

圖片來源:https://time.geekbang.org/column/article/306548

本文總結

  • 分片集群誕生理由:寫性能在高並發下會遇到瓶頸&&無法無限地縱向擴展(不划算)

  • 分片集群:需要解決「數據路由」和「數據遷移」的問題

  • Redis Cluster數據路由

    • Redis Cluster默認一個集群有16384個哈希槽,哈希槽會被分配到Redis集群中的實例中
    • Redis集群的實例會相互「通訊」,交互自己所負責哈希槽信息(最終每個實例都有完整的映射關系)
    • 當客戶端請求時,使用CRC16算法算出Hash值並模以16384,自然就能得到哈希槽進而得到所對應的Redis實例位置
  • 為什么16384個哈希槽:16384個既能讓Redis實例分配到的數據相對均勻,又不會影響Redis實例之間交互槽信息產生嚴重的網絡性能開銷問題

  • Redis Cluster 為什么使用哈希槽,而非一致性哈希算法:哈希槽實現相對簡單高效,每次擴縮容只需要動對應Solt(槽)的數據,一般不會動整個Redis實例

  • Codis數據路由:默認分配1024個哈希槽,映射相關信息會被保存至Zookeeper集群。Proxy會緩存一份至本地,Redis集群實例發生變化時,DashBoard更新Zookeeper和Proxy的映射信息

  • Redis Cluster和Codis數據遷移:Redis Cluster支持同步遷移,Codis支持同步遷移&&異步遷移

    • 把新的Redis實例加入到集群中,然后把部分數據遷移到新的實例上(在線)

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

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

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

原創不易!!求三連!!


免責聲明!

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



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