滴滴 Redis 異地多活的演進歷程

爲了更好的做好容災保障,使業務能夠應對機房級別的故障,滴滴的存儲服務都在多機房進行部署。本文簡要分析了 Redis 實現異地多活的幾種思路,以及滴滴 Redis 異地多活架構演進過程中遇到的主要問題和解決方法,拋磚引玉,給小夥伴們一些參考。

Redis 異地多活的主要思路

業界實現 Redis 異地多活通常三種思路:主從架構、Proxy 雙寫架構、數據層雙向同步架構。

主從架構

主從架構的思路:

  1. 各機房的 Redis 通過 Proxy 對外提供讀寫服務,業務流量讀寫本機房的 Redis-proxy

  2. 主機房裏的 Redis-master 實例承擔所有機房的寫流量

  3. 從機房裏的 Redis-slave 實例只讀,承擔本機房裏的讀流量

主從架構的優點

主從架構的缺點

Proxy 雙寫架構  

Proxy 雙寫架構的思路:

  1. 各機房的 Redis 通過 Proxy 對外提供讀寫服務,業務流量讀寫本機房的 Redis-proxy

  2. 不區分主從機房,每個機房都是獨立的 Redis 集羣

  3. 各機房的讀寫流量都是訪問本機房的 Redis 集羣

  4. Proxy 層在寫本機房成功後,將寫請求異步發送到對端機房

Proxy 雙寫架構的優點:

Proxy 雙寫架構的缺點:

數據層雙向同步架構   

數據層雙向同步架構的思路:

  1. Proxy 不關心底層 Redis 數據同步

  2. 業務流量只訪問本機房裏的 Redis 集羣

  3. 在 RedisServer 層面實現數據同步

數據層雙向同步架構的優點:

數據層雙向同步架構的缺點:

滴滴 Redis 架構

Codis 架構(早期架構,現已廢棄)

Kedis 架構(線上架構)

滴滴 Redis 異地多活架構的演進

第一代多活架構       

第一代 Redis 多活基於 Codis 架構在 proxy 層實現了雙寫,即本機房的 Proxy 將寫流量轉發到對端機房的 Proxy,這個方案的特點是快速實現,儘快滿足了業務多機房同步的需求。如前面 Proxy 雙向架構思路所講,本方案還存在着諸多缺點,最主要的是網絡故障時,同步數據丟失的問題,爲了解決這些問題,我們開發了第二代多活架構。

第二代多活架構

第二代多活基於 Kedis 架構,對 Redis-server 進行改造,可以把增量數據從 Redis 直接寫入本機房的 MQ 中,由對端機房的 consumer 來消費 MQ,consumer 將數據寫入對端 Redis 中。網絡故障時,數據會在 MQ 堆積,待網絡恢復後,consumer 可以基於故障前的 offset 繼續進行消費,寫入對端 Redis,從而保證在網絡故障時 Redis 多活不會丟數據。

但這一代架構仍不夠完美,存在以下問題:

爲了解決以上問題,我們開發了第三代架構。

第三代多活架構

在第三代架構中,我們細化了設計目標,主要思路是保證同步鏈路中的數據不丟不重,同時去掉對 MQ 的依賴,降低多活成本。

第三代架構中,我們去掉了 MQ 和 consumer,新增了 syncer 組件。syncer 組件模擬 Redis-slave 從 Redis-master 中拉取增量數據,這樣把數據同步和 Redis 進行解耦,便於後續多機房擴展。

在第三代架構中,Redis 遇到了迴環、重試、數據衝突、增量數據存儲和讀取等問題,接下來一一介紹我們應對這些問題的解決方案。

1、迴環問題

機房 - A 寫入的數據同步到機房 - B,防止數據再傳回機房 - A。

爲了解決迴環問題,我們開發了防迴環機制:

  1. Redis 增加 shardID 配置,標識唯一分片號

  2. Redis 請求中增加 opinfo,記錄元信息,包含 shardID   

2、重試問題

機房 - A 寫入的 incrby 請求同步到機房 - B,由於中間鏈路的重試,導致機房 - B 可能執行了多次。 

爲了解決重試問題,我們開發了防重放機制:

  1. Redis 增加 opid,標識唯一請求號

  2. Redis 請求中增加 opinfo,記錄元信息 [opid]

3、數據衝突問題

雙機房同時修改同一個 key 導致數據不一致

對於數據衝突,不同數據類型的不同操作的數據合併,如果單從存儲層解決,是一個非常複雜的話題。如果業務層做了單元化部署,則不會出現這種問題。如果業務層沒有做單元化,我們開發了衝突檢測功能,來幫助業務及時發現數據衝突,最後數據以哪邊爲準來修正,需要業務同學來決策。

衝突檢測機制:

  1. Redis 記錄 key 的最後 write 時間

  2. Redis 請求中增加 opinfo,記錄元信息 [timestamp]

  3. 如果 opinfo.timestamp<=key_write_time,則記錄衝突 key

時間 T1<T2<T3

以上是衝突檢測的基本原理,這是一個旁路統計,幫助用戶發現一些潛在衝突數據。

4、增量數據存儲和讀取問題

因爲 syncer 只是同步組件,不會存儲數據,所以需要考慮當網絡故障時,增量數據的存儲和讀取問題。

爲了解決這個問題,我們對 Redis 的 aof 機制進行了改造,可以在網絡故障時,增量數據都堆積在 Redis 的磁盤上,在網絡恢復後,syncer 從 Redis 里拉取增量 aof 數據發送到對端機房,避免數據丟失。

aof 機制改造有:aof 文件切分、aof 增量複製、aof 異步寫盤

開源 Redis 是在主線程中進行 aof 寫盤,當磁盤 IO 過高時,Redis 寫盤可能造成業務訪問 Redis 耗時抖動。因此我們開發了 aof 異步寫盤機制:

這樣 Redis 的訪問耗時不受磁盤 IO 的影響,更好的保證穩定性。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/UcyO3J0XEEe1sH3frmmNDg