前言
Redis 作為一種高性能的內存數據庫,普遍用于目前主流的分布式架構系統中。為了提高系統的容錯率,使用多實例的 Redis 也是必不可免的,但同樣復雜度也相比單實例高出很多。本文主要會介紹 Redis 在多機數據庫下的三種實現。
主從模式
Redis 的主從模式指的就是主從復制。
用戶可以通過 SLAVEOF 命令或者配置的方式,讓一個服務器去復制另一個服務器即成為它的從服務器。
主從模式架構
Redis 如何實現主從模式?
Redis 的從服務器在向主服務器發起同步時,一般會使用 SYNC 或 PSYNC 命令。
初次同步
當從服務器收到 SLAVEOF 命令后,會向其主服務器執行同步操作,進入主從復制流程。
- 從服務器向主服務器發起SYNC 或 PSYNC 命令
- 主服務器執行 BGSAVE命令,生成 RDB 文件,并使用緩存區記錄從現在開始的所有寫命令
- RDB 文件生成完成后,主服務器會將其發送給從服務器
- 從服務器載入 RDB 文件,將自己的數據庫狀態同步更新為主服務器執行 BGSAVE命令時的狀態。
- 主服務器將緩沖區的所有寫命令發送給從服務器,從服務將執行這些寫命令,數據庫狀態同步為主服務器最新狀態。
SYNC 與 PSYNC 的區別
當主從同步完成后,如果此時從服務器宕機了一段時間,重新上線后勢必要重新同步一下主服務器,SYNC與 PSYNC命令的區別就在于斷線后重復制階段處理的方式不同。
- SYNC
從服務器重新向主服務器發起 SYNC命令,主服務器將所有數據再次重新生成 RDB 快照發給從服務器開始同步
- PSYNC
從服務器重新向主服務器發起 PSYNC命令。主服務器根據雙方數據的偏差量判斷是否是需要完整重同步還是僅將斷線期間執行過的寫命令發給從服務器。
明顯可以發先 PSYNC 相比 SYNC 效率好很多,要知道同步所有數據是一個非常費資源(磁盤 IO,網絡)的操作,而如果只是因為短暫網絡不穩定就同步所有資源是非常不值的。因此 Redis 在 2.8 版本后都開始使用 PSYNC 進行復制
PSYNC 如何實現部分重同步?
實現部分重同步主要靠三部分
1. 記錄復制偏移量
主服務器與從服務器都會維護一個復制偏移量。
- 當主服務器向從服務器發送 N 個字節的數據后,會將自己的復制偏移量 +N。
- 當從服務器收到主服務器 N 個字節大小數據后,也會將自己的復制偏移量 +N。
當主從雙方數據是同步時,這個偏移量是相等的。而一旦有個從服務器斷線一段時間而少收到了部分數據。那么此時主從雙方的服務器偏移量是不相等的,而他們的差值就是少傳輸的字節數量。如果少傳輸的數據量不是很大,沒有超過主服務器的復制積壓緩沖區大小,那么將會直接將緩沖區內容發送給從服務器避免完全重同步。反之還是需要完全重同步的。
2. 復制積壓緩沖區
復制積壓緩沖區是由主服務器維護的一個先進先出的字節隊列,默認大小是 1mb。每當向從服務器發送寫命令時,都會將這些數據存入這個隊列。每個字節都會記錄自己的復制偏移量。從服務器在重連時會將自己的復制偏移量發送給主服務器,如果該復制偏移量之后的數據存在于復制積壓緩沖區中,則僅需要將之后的數據發送給從服務器即可。
3. 記錄服務器 ID
當執行主從同步時,主服務器會將自己的服務器 ID (一般是自動生成的 UUID ) 發送給從服務器。從服務器在斷線恢復后會判斷該 ID 是否為當前連接的主服務器。如果是同一個 ID 則代表主服務器沒變嘗試部分重同步。如果不是同一個 ID 代表主服務有變動,則會與主服務器完全重同步。
具體流程圖如下:
Redis 哨兵模式 (Sentinel)
Redis 主從模式雖然能做到很好的數據備份,但是他并不是高可用的。一旦主服務器點宕機后,只能通過人工去切換主服務器。因此 Redis 的哨兵模式也就是為了解決主從模式的高可用方案。
哨兵模式引入了一個 Sentinel 系統去監視主服務器及其所屬的所有從服務器。一旦發現有主服務器宕機后,會自動選舉其中的一個從服務器升級為新主服務器以達到故障轉義的目的。
同樣的 Sentinel 系統也需要達到高可用,所以一般也是集群,互相之間也會監控。而 Sentinel 其實本身也是一個以特殊模式允許 Redis 服務器。
實現原理
1.Sentinel 與主從服務器建立連接
- Sentinel 服務器啟動之后便會創建于主服務器的 命令連接 ,并訂閱主服務器的 sentinel:hello 頻道以創建 訂閱連接
- Sentinel 默認會每 10 秒向主服務器發送 INFO 命令,主服務器則會返回主服務器本身的信息,以及其所有從服務器的信息。
- 根據返回的信息,Sentinel 服務器如果發現有新的從服務器上線后也會像連接主服務器時一樣,向從服務器同時創建命令連接與訂閱連接。
2.判定主服務器是否下線
每一個 Sentinel 服務器每秒會向其連接的所有實例包括主服務器,從服務器,其他 Sentinel 服務器)發送 PING命令,根據是否回復 PONG 命令來判斷實例是否下線。
判定主觀下線
如果實例在收到 PING命令的 down-after-milliseconds 毫秒內(根據配置),未有有效回復。則該實例將會被發起 PING命令的 Sentinel 認定為主觀下線。
判定客觀下線
當一臺主服務器被某個 Sentinel 服務器判定為客觀下線時,為了確保該主服務器是真的下線, Sentinel 會向 Sentinel 集群中的其他的服務器確認,如果判定主服務器下線的 Sentinel 服務器達到一定數量時(一般是 N/2+1),那么該主服務器將會被判定為客觀下線,需要進行故障轉移。
3.選舉領頭 Sentinel
當有主服務器被判定客觀下線后,Sentinel 集群會選舉出一個領頭 Sentinel 服務器來對下線的主服務器進行故障轉移操作。整個選舉其實是基于 RAFT 一致性算法而實現的,大致的思路如下:
- 每個發現主服務器下線的 Sentinel 都會要求其他 Sentinel 將自己設置為局部領頭 Sentinel。
- 接收到的 Sentinel 可以同意或者拒絕
- 如果有一個 Sentinel 得到了半數以上 Sentinel 的支持則在此次選舉中成為領頭 Sentinel。
- 如果給定時間內沒有選舉出領頭 Sentinel,那么會再一段時間后重新開始選舉,直到選舉出領頭 Sentinel。
4.選舉新的主服務器
領頭服務器會從從服務中挑選出一個最合適的作為新的主服務器。挑選的規則是:
- 選擇健康狀態的從節點,排除掉斷線的,最近沒有回復過 INFO命令的從服務器。
- 選擇優先級配置高的從服務器
- 選擇復制偏移量大的服務器(表示數據最全)
挑選出新的主服務器后,領頭服務器將會向新主服務器發送 SLAVEOF no one命令將他真正升級為主服務器,并且修改其他從服務器的復制目標,將舊的主服務器設為從服務器,以此來達到故障轉移。
Redis Cluster
Redis 哨兵模式實現了高可用,讀寫分離,但是其主節點仍然只有一個,即寫入操作都是在主節點中,這也成為了性能的瓶頸。
因此 Redis 在 3.0 后加入了 Cluster 模式,它采用去無心節點方式實現,集群將會通過分片方式保存數據庫中的鍵值對
節點
一個 Redis 集群中會由多個節點組成,每個節點都是互相連接的,會保存自己與其他節點的信息。節點之間通過 gossip 協議交換互相的狀態,以及保新加入的節點信息。
數據的 Sharding
Redis Cluster 的整個數據庫將會被分為 16384 個哈希槽,數據庫中的每個鍵都屬于這 16384 個槽中的其中一個,集群中的每個節點可以處 0 個或者最多 16384 個槽。
設置槽指派
通過命令 CLUSTER ADDSLOTS
如 127.0.0.1:7777> CLUSTER ADDSLOTS 1 2 3 4 5 命令就是將 1,2,3,4,5 號插槽指派給本地端口號為 7777 的節點負責。
設置后節點將會將槽指派的信息發送給其他集群,讓其他集群更新信息。
計算鍵屬于哪個槽
- def slot_number(key):
- return CRC16(key) & 16383
計算哈希槽位置其實使用的是 CRC16 算法對鍵值進行計算后再對 16383 取模得到最終所屬插槽。
也可以使用 CLUSTER KEYSLOT
Sharding 流程
- 當客戶端發起對鍵值對的操作指令后,將任意分配給其中某個節點
- 節點計算出該鍵值所屬插槽
- 判斷當前節點是否為該鍵所屬插槽
- 如果是的話直接執行操作命令
- 如果不是的話,向客戶端返回 moved 錯誤,moved 錯誤中將帶著正確的節點地址與端口,客戶端收到后可以直接轉向至正確節點
Redis Cluster 的高可用
Redis 的每個節點都可以分為主節點與對應從節點。主節點負責處理槽,從節點負責復制某個主節點,并在主節點下線時,代替下線的主節點。
如何實現故障轉移
其實與哨兵模式類似,Redis 的每個節點都會定期向其他節點發送 Ping 消息,以此來檢測對方是否在線。當一個節點檢測到另一個節點下線后,會將其設置為疑似下線。如果一個機器中,有半數以上的節點將某個主節點設為疑似下線,則該節點將會被標記為已下線狀態,并開始執行故障轉移。
- 通過 raft 算法從下線主節點的從節點中選出新的主節點
- 被選中的從節點執行 SLAVEOF no one 命令,成為新的主節點
- 新的主節點撤銷掉已下線主節點的槽指派,并將這些槽指給自己
- 新的主節點向集群中廣播自己由從節點變為主節點
- 新的主節點開始接受和負責自己處理槽的有關命令請求
總結
本文主要介紹了 Redis 三種集群模式,總結一下
主從模式 可以實現讀寫分離,數據備份。但是并不是「高可用」的
哨兵模式 可以看做是主從模式的「高可用」版本,其引入了 Sentinel 對整個 Redis 服務集群進行監控。但是由于只有一個主節點,因此仍然有寫入瓶頸。
Cluster 模式 不僅提供了高可用的手段,同時數據是分片保存在各個節點中的,可以支持高并發的寫入與讀取。當然實現也是其中最復雜的。
原文地址:https://mp.weixin.qq.com/s?__biz=MzUxOTE5MTY4MQ==&mid=2247485864&idx=1&sn=7634af55240fe1107f44afb602d25aa3&chksm=f9fc2446ce8bad509a74322c2b3896d6b9dcc50269367b76efa223997507741fc6dd8b84b7a0&mpshare=1&