博客 / 詳情

返回

獨家深度解析redis集羣的架構問題(附腦洞)

本文首發自[慕課網](imooc.com) ,想了解更多IT乾貨內容,程序員圈內熱聞,歡迎關注"慕課網"及“慕課網公眾號”!  

作者:一凡|慕課網講師


Redis 是一種開源(BSD 許可)、數據結構存儲在內存中的系統,用作數據庫、緩存和消息隊列。Redis 提供了諸如字符串、散列、列表、集合、帶範圍查詢的排序集合、位圖、超級日誌、地理空間索引和流等數據結構。Redis 內置複製、Lua 腳本、LRU 驅逐、事務和不同級別的磁盤持久化,並通過 Redis Sentinel 和 Redis Cluster 自動分區提供高可用性。

1 集羣的優勢

下面是redis集羣的幾個明顯優勢。

1.1 伸縮性,數據規模不斷增大的時候,容易擴容

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a8c58f990bed45f7a165a70f831cdb35~tplv-k3u1fbpfcp-zoom-1.image

單實例模式:只能垂直擴展,增大機器內存的容量;

集羣模式:支持垂直擴展,也支持水平擴展,有更好的靈活性,也可以支持更大的容量;

1. 2 高可用,服務故障的情況,影響範圍小

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9de5111b6b7f427bbf4f8af76eec3187~tplv-k3u1fbpfcp-zoom-1.image

單實例模式:故障轉移前100%不可用(slave轉換為master之前);

集羣模式:故障轉移前部分不可用(集羣規模越大,故障影響越小); 

1. 3 高性能,查詢和寫入的性能 

單實例模式:查詢可以分散在多個slave,寫入卻只有一個master;

集羣模式:查詢有多個master和多個slave,寫入也有多個master;

2 數據分片,一致性hash

實現redis集羣的核心點,是針對數據的分片,這裏的一致性hash算法就非常關鍵。

2.1 普通的hash 算法

node=hash(key)%number

數量變化和node順序變化,導致node選擇的差異性巨大,造成巨大的緩存失效。

2.2 一致性hash

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5a449244b26a40fb9748da41c815b75d~tplv-k3u1fbpfcp-zoom-1.image

hash(node) 形成虛擬節點環,hash(key)落在虛擬節點環,找到對應的node。

由於hash(node)的穩定性,與node順序無關。node變更隻影響一小部分數據。

2.3 redis cluster的hash slot算法

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/51936eeb67a24d23875974108c3e67f3~tplv-k3u1fbpfcp-zoom-1.image

關係: cluster > node > slot > key

Redis Cluster在設計中沒有使用一致性哈希(Consistency Hashing),而是使用數據分片引入哈希槽(hash slot)來實現。

一個 Redis Cluster包含16384(0~16383)個哈希槽,存儲在Redis Cluster中的所有鍵都會被映射到這些slot中。

集羣中的每個鍵都屬於這16384個哈希槽中的一個,集羣使用公式slot=CRC16(key)/16384來計算key屬於哪個槽,其中CRC16(key)語句用於計算key的CRC16 校驗和。

按照槽來進行分片,通過為每個節點指派不同數量的槽,可以控制不同節點負責的數據量和請求數。

3 集羣元數據的一致性

3.1 對比:集中式存儲元數據

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bbd486baeac14690a6f956f9f47a696a~tplv-k3u1fbpfcp-zoom-1.image

依賴外部的集中式存儲服務,比如:zookeeper, etcd等,會增加運維負擔和系統複雜度。

集中式的好處在於,元數據的讀取和更新,時效性非常好,一旦元數據出現了變更,就立即更新到集中式的存儲中,其它節點讀取的時候就可以感知到;不好在於,所有的元數據的更新壓力全部集中在一個地方,可能會導致元數據的存儲有壓力。

3.2 gossip 協議來廣播自己的狀態以及自己對整個集羣認知的改變

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ab84e6d748cf467dae975f125226fcb7~tplv-k3u1fbpfcp-zoom-1.image

ping / pong 消息來確認節點的存活和同步全部的集羣元數據。

集羣元數據,包括:master/slave node列表和狀態, slot與node關係。

每個master node每秒會執行 10 次 ping,每次會選擇 5 個最久沒有通信的其它節點。

定時檢查全部節點,如果發現某個節點通信延時達到了 cluster_node_timeout / 2,那麼立即發送 ping,避免數據交換延時過長。

3.3 增加新節點

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/46f0734a39464fb781f4a6fa3ecfcb66~tplv-k3u1fbpfcp-zoom-1.image

命令 CLUSTER MEET <ip> <port> 

向一個節點 node 發送 CLUSTER MEET 命令,可以讓 node 節點與 ip 和 port 所指定的節點進行握手(handshake),當握手成功時,node 節點就會將 ip 和 port 所指定的節點添加到 node 節點當前所在的集羣中。

按照gossip協議,廣播meet消息,全部節點接收新節點。

重新hash(node),分配和轉移相應的slot給到新節點。

4 客户端如何調用

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/40e6b8fca722404da410ee7e4109fd05~tplv-k3u1fbpfcp-zoom-1.image

4.1 hash slot 信息

當客户端連接任何一個實例,實例就將哈希槽與實例的映射關係響應給客户端,客户端就會將哈希槽與實例映射信息緩存在本地。

4.2 請求數據

當客户端請求時,會計算出鍵所對應的哈希槽,在通過本地緩存的哈希槽實例映射信息定位到數據所在實例上,再將請求發送給對應的實例。 

4.3 重定向機制

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6e4ad1777fcc45b39488129187650fb4~tplv-k3u1fbpfcp-zoom-1.image

客户端將請求發送到實例上,這個實例沒有相應的數據,該 Redis 實例會告訴客户端更新本地的哈希槽與映射信息的緩存,將請求發送到其他的實例上。

5 新的節點加入集羣(擴容)

同3.3 增加新節點的前序步驟是一樣的。

這裏詳細瞭解下重新擴容時slot遷移和數據。

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/76d753a8e7dd4ea58d94c81a5b07035b~tplv-k3u1fbpfcp-zoom-1.image

5.1 每個slot的遷移過程如下所示:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a673028c008541dfa612c591dc131b8a~tplv-k3u1fbpfcp-zoom-1.image

  • 對目標節點發送cluster setslot {slot_id} importing {sourceNodeId}命令,目標節點的狀態被標記為"importing",準備導入這個slot的數據。
  • 對源節點發送cluster setslot {slot_id} migrating {targetNodeID}命令,源節點的狀態被標記為"migrating",準備遷出slot的數據。
  • 源節點執行cluster getkeysinslot {slot_id} {count}命令,獲取這個slot的所有的key列表(分批獲取,count指定一次獲取的個數),然後針對每個key進行遷移。
  • 在源節點執行migrate {targetIp} {targetPort} "" 0 {timeout} keys {keys}命令,把一批批key遷移到目標節點(redis-3.0.6之前一次只能遷移一個key),具體來説,源節點對遷移的key執行dump指令得到序列化內容,然後通過客户端向目標節點發送攜帶着序列化內容的restore指令,目標節點進行反序列化後將接收到的內容存入自己的內存中,目標節點給客户端返回"OK",然後源節點刪除這個key,這樣,一個key的遷移過程就結束了。
  • 所有的key都遷移完成後,一個slot的遷移就結束了。
  • 遷移所有的slot(應該被遷移的那些),所有的slot遷移完成後,新的集羣的slot就重新分配完成了,向集羣內所有master發送cluster setslot {slot_id} node {targetNodeId}命令,通知他們哪些槽被遷移到了哪些master上,讓它們更新自己的信息。

5.2 slot遷移的其他説明

  • 遷移過程是同步的,在目標節點執行restore指令到原節點刪除key之間,原節點的主線程處於阻塞狀態,直到key被刪除成功。
  • 如果遷移過程突然出現網路故障,整個slot遷移只進行了一半,這時兩個節點仍然會被標記為中間過濾狀態,即"migrating"和"importing",下次遷移工具連接上之後,會繼續進行遷移。
  • 在遷移過程中,如果每個key的內容都很小,那麼遷移過程很快,不會影響到客户端的正常訪問。
  • 如果key的內容很大,由於遷移一個key的遷移過程是阻塞的,就會同時導致原節點和目標節點的卡頓,影響集羣的穩定性,所以,集羣環境下,業務邏輯要儘可能的避免大key的產生 。

5.3 slot編號

無需要求每個master的slot編號是連續的,只要每個master管理的slot的數量均衡就可以。

5.4 減少節點(縮容)

縮容的過程與擴容類似,只是slot和數據從下線的節點內轉移到其他的節點上。

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d46f6cdb9c51428f93cc2457845c1ae3~tplv-k3u1fbpfcp-zoom-1.image

6 集羣中機器出現故障

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2f60902fa22e432f9aa0a2878fa69f16~tplv-k3u1fbpfcp-zoom-1.image

6.1 故障檢測,節點失效

如果一個節點認為另外一個節點宕機,那麼就是 pfail,主觀宕機;

如果超過半數的節點都認為另外一個節點宕機了,那麼就是 fail,客觀宕機;

6.2 故障轉移,節點選舉

每個slave節點,都根據自己對 master 複製數據的 offset,來設置一個選舉時間,offset 越大(複製數據越多)的從節點,選舉時間越靠前,優先進行選舉。

所有的 master node 開始 slave 選舉投票,給要進行選舉的 slave 進行投票,如果大部分 master node(N/2 + 1)都投票給了某個從節點,那麼選舉通過,那個從節點可以切換成 master。

從節點執行主備切換,從節點切換為主節點。

7 主從同步以及高可用

redis的主從同步在cluster版本之前就存在了,既可以提供更高的查詢效率(多slave可以查詢),又可以增加服務的可用性(master掛機後可以啓用slave成為master)。

7.1 使用主從架構時

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/abf5985e1ea84f1891dfc079c1799b7c~tplv-k3u1fbpfcp-zoom-1.image

將只有一個 Master 和多個從屬用於複製。

所有寫入都轉到主節點,這會在主節點上產生更多負載。

如果Master宕機,整個架構容易出現SPOF(單點故障)。

當您的用户羣增長時,MS 架構無助於擴展。

所以我們需要一個進程來在發生故障或關閉的情況下監控 Master,那就是 Sentinel。

7.2 Redis哨兵

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/088afa983b1b466f92e7f7c64d49d035~tplv-k3u1fbpfcp-zoom-1.image

故障轉移處理

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7b8f4fc8ebb94351aa819c385762b0a3~tplv-k3u1fbpfcp-zoom-1.image

8 主從同步與數據一致性

8.1 主從同步的實現過程

主從同步分為 2 個步驟:同步和命令傳播。

數據同步有sync和psync。

sync全量同步,性能比較差;psync增量同步,速度和實時性好很多。

8.2 全量同步 sync

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bc962f8b2d9d4c97860ef7f0bda22ce8~tplv-k3u1fbpfcp-zoom-1.image

從服務器對主服務的同步操作,需要通過 sync 命令來實現,以下是 sync 命令的執行步驟:

  • 從服務器向主服務器發送 sync 命令
  • 收到 sync 命令後,主服務器執行 bgsave 命令,用來生成 rdb 文件,並在一個緩衝區中記錄從現在開始執行的寫命令。
  • bgsave 執行完成後,將生成的 rdb 文件發送給從服務器,用來給從服務器更新數據
  • 主服務器再將緩衝區記錄的寫命令發送給從服務器,從服務器執行完這些寫命令後,此時的數據庫狀態便和主服務器一致了。

8.3 部分重同步 psync

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1f9c07155b5a410cbd8b062644cc8ac7~tplv-k3u1fbpfcp-zoom-1.image

部分重同步功能由以下 3 部分組成:

  • 主從服務器的複製偏移量
  • 主服務器的複製積壓緩衝區
  • 服務器的運行 id(run id)

8.4 心跳檢測

當完成了同步之後,主從服務器就會進入命令傳播階段,此時從服務器會以每秒 1 次的頻率,向主服務器發送命令:REPLCONF ACK <replication_offset> 其中 replication_offset 是從服務器當前的複製偏移量

發送這個命令主要有三個作用:

  • 檢測主從服務器的網絡狀態
  • 輔助實現 min-slaves 選項
  • 檢測命令丟失(若丟失,主服務器會將丟失的寫命令重新發給從服務器)

8.5 主從同步總結

發送 SLAVEOF 命令可以進行主從同步,比如:SLAVEOF 127.0.0.1 6379

主從同步有同步和命令傳播 2 個步驟

  • 同步:將從服務器的數據庫狀態更新成主服務器當前的數據庫狀態(一個消耗資源的操作)
  • 命令傳播:當主服務器數據庫狀態被修改後,導致主從服務器數據庫狀態不一致,此時需要讓主從數據同步到一致的過程

主從同步分初次複製和斷線後重複製兩種情況

  • 從 2.8 版本開始,在出現斷線後重複製情況時,主服務器會根據複製偏移量、複製積壓緩衝區和 run id,來確定執行完整重同步還是部分重同步
  • 2.8 版本使用 psync 命令來代替 sync 命令去執行同步操作。目的是為了解決同步(sync 命令)的低效操作

問題1:集羣的規模能否無限大,比如:1w台機器?

答案是否定的,redis 官方給的 Redis Cluster 的規模上限是 1000 個實例。

限制的原因,關鍵在於實例間的通信開銷,集羣中的每個節點都保存所有哈希槽與節點對應關係信息(Slot 映射到節點的表),以及自身的狀態信息。

參照3.2gossip協議廣播方式,節點越多,廣播風暴對於網絡以及服務器壓力也就越大。

雖然可以設置廣播消息同步的超時時間,但是節點增多、超時時間變長之後,數據一致性的消息同步延時也會更大,出現元數據不一致的可能性也會增加。

問題2:從庫的使用,以及如何權衡?

從庫的作用,一是提高可用性,當主庫宕機之後,可以立即啓用從庫作為主庫提供服務;一是提高伸縮性,提高了數據查詢併發能力,從庫提供查詢服務就增加了服務資源,更多的節點來支持查詢。

由於主從同步存在數據一致性問題,所以在使用從庫的過程中,相應的也就會遇到一些問題。

比如:因為從庫數據同步慢了,這時候主庫宕機了,數據不完整的從庫作為主庫,就會出現數據丟失的情況。從庫用來查詢也有類似問題,實時寫入的新數據,同步到從庫可能會有延時,在數據沒有同步到從庫的時候查詢從庫,也會出現查詢無數據的情況。

所以在使用從庫的情況下,需要考慮到上面的問題。

面對宕機的時候,數據丟失的問題,內存型數據庫都會存在的風險,使用redis都需要面對這個風險,否則就要犧牲性能高正數據一致性,redis數據先持久化再提供服務,這樣性能就會下降非常明顯了,沒法滿足內存性數據庫的優勢了。

啓用從庫查詢,可以針對一些數據更新的實時性較低,對於髒數據不那麼敏感的業務,或者查詢量實在太大而可以忽略部分數據延時的影響。

問題3:redis集羣化之後,代理的必要性?

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/491f0b9707d5402a8bfc3af21200b35d~tplv-k3u1fbpfcp-zoom-1.image

代理的性能和穩定性同樣是問題所在。

產品的運維難度以及持續的維護,還是官方的redis cluster更加可靠。

有條件的團隊,針對redis cluster的不足,還會有更深入的優化,比如咱們自己研發的tendis。

問題4:單key的百萬qps限頻問題?(待解)

單key的頻繁更新,由於單個key有且只能落地到一個master節點的一個slot上面,無法通過增加節點增加slave的方法擴容,性能瓶頸就會受限於機器的CPU/內存的讀寫能力了。

假設單機最高10w的寫如速度,那麼,要實現接口的100w的qps限頻功能,要怎麼實現呢?

請輸出你的解答。。。

附-腦洞:三體人來到地球,要消滅近半的地球人(待解)

三體人不像滅霸,直接用手套簡單粗暴的一個響指就毀滅一半生靈,而是給每個人一個選擇,選擇對的人就有機會生存下來。

這個選擇的方法也特別簡單,但是需要開發一套系統來支持。

那麼這套系統的開發工作就落在了地球人最聰明的程序員你的頭上。

開發好了可以讓自己以及家人獲得豁免權而生存下來,開發的不好,直接咔嚓。

這個選擇的描述如下

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/051764b85f2e47afae18f4792a842942~tplv-k3u1fbpfcp-zoom-1.image

全球有一個三體人的燈球,默認是熄滅狀態。

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/262df0e309cc4c608d1311a6aa6278d4~tplv-k3u1fbpfcp-zoom-1.image

每個人會有一個三體人的開關,上面會顯示當前燈球的狀態(熄滅或者亮起),有兩個操作按鈕,分別是控制燈球的熄滅和亮起。每個人只有一次選擇機會,兩個按鈕只能選擇一個按鈕,按一次。如果一個按鈕都不選擇,不按的話,無論燈球最終狀態如何,都是要被消滅。小孩子或者老人、殘疾人,可以由監護人來輔助其完成選擇。

系統實現的前提條件和需求

前提條件説明

1 全球80億人口,必須同時在1分鐘時間內完成選擇(三體人的開關,全球實時狀態同步,無時間偏差,無時延),規定時間範圍之外無法操作;

2 三體人提供1000台128核256G內存1T磁盤的服務器,三體人的開關與服務器的網絡是直連的,沒有時延,沒有網絡開銷;

3 全球80億人,每個人都有一個唯一的從0開始自增的數字ID,與三體人的開關也是一一對應的;

4 每個人的ID%1000指向特定服務器,請求系統提供的接口 /vote

系統需求

功能,開發這個投票接口(所有請求只會在這1分鐘內請求)

/vote?uid=1111&click=1&t=1234143134134134134

參數

uid 長整型,每個人的唯一ID

click 枚舉值,按鈕選擇,0 熄滅,1 亮起

t 長整型,操作時間,單位納秒

處理

1 在同一個時刻(同一納秒),如果有多個人操作,選擇次數多的生效,如果2個選擇次數相同,狀態不變。如:熄滅2次,亮起3次,這個時刻的狀態是亮起。

結果數據

1 最終燈球的狀態,是熄滅,還是亮起;

2 選擇正確的人(ID集合);

3 選擇錯誤的人(ID集合);

4 沒有做出選擇的人(ID集合);

最終執行

調用三體人在服務器上安裝的系統程序 ,完成地球人消滅計劃。

kill uid

調用三體人的系統程序無延時,等同於內存讀取的效率。

要求在1分鐘時間內,把選擇錯誤的人和沒有做出選擇的人消滅掉。

模擬測試

1 三體人在1分鐘內導入測試用例,完成80億人的選擇。

2 1分鐘正確執行完成 kill 調用。

如果無法實現上述工作,失敗。

請輸出你的解答。。。


歡迎關注「慕課網」官方帳號,我們會一直堅持提供IT圈優質內容,分享乾貨知識,大家一起共同成長吧!

本文原創發佈於慕課網 ,轉載請註明出處,謝謝合作

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.