滴滴 Redis 異地多活的演進歷程
為了更好的做好容災保障,使業務能夠應對機房級別的故障,滴滴的儲存服務都在多機房進行部署。本文簡要分析了 Redis 實現異地多活的幾種思路,以及滴滴 Redis 異地多活架構演進過程中遇到的主要問題和解決方法,拋磚引玉,給小夥伴們一些參考。
Redis 異地多活的主要思路
業界實現 Redis 異地多活通常三種思路:主從架構、Proxy雙寫架構、資料層雙向同步架構。
主從架構
主從架構的思路:
各機房的 Redis 透過 Proxy 對外提供讀寫服務,業務流量讀寫本機房的 Redis-proxy
主機房裡的 Redis-master 例項承擔所有機房的寫流量
從機房裡的 Redis-slave 例項只讀,承擔本機房裡的讀流量
主從架構的優點:
實現簡單,在 Proxy 層開發讀寫分流功能就可以實現
Redis 層使用原生主從複製,可以保證資料一致性
主從架構的缺點:
從機房裡的 Redis-proxy 需要跨機房寫,受網路延時影響,業務在從機房裡的寫耗時高於主機房
主機房故障時,從機房的寫流量也會失敗,需要把從機房切換為主機房,切換 Redis-master
網路故障時,從機房的寫流量會全部失敗,為了保障資料一致性,這種場景比較難處理
Proxy 雙寫架構
Proxy 雙寫架構的思路:
各機房的 Redis 透過 Proxy 對外提供讀寫服務,業務流量讀寫本機房的 Redis-proxy
不區分主從機房,每個機房都是獨立的 Redis 叢集
各機房的讀寫流量都是訪問本機房的 Redis 叢集
Proxy 層在寫本機房成功後,將寫請求非同步傳送到對端機房
Proxy 雙寫架構的優點:
實現簡單,在 Proxy 層開發雙寫功能就可以實現
一個機房故障時,其他機房的流量不受影響
網路故障時,各機房內部的流量也不受影響
Proxy 雙寫架構的缺點:
不能保證資料一致性,Proxy 非同步 write 請求可能會失敗,失敗丟棄請求後,導致雙機房資料不一致
假設機房-A的叢集先上線,機房-B 後上線,Proxy 雙寫架構不能支援把機房-A的存量資料同步到機房-B
網路故障時,非同步 write 會失敗後丟棄,網路恢復後,之前失敗的資料已經丟棄,導致雙機房資料不一致
資料層雙向同步架構
資料層雙向同步架構的思路:
Proxy 不關心底層 Redis 資料同步
業務流量只訪問本機房裡的 Redis 叢集
在 RedisServer 層面實現資料同步
資料層雙向同步架構的優點:
機房-A故障時,機房-B不受影響,反向如是
網路故障時,本機房流量不受影響,網路恢復後,資料層面可以拉取增量資料繼續同步,資料不丟
支援存量資料的同步
業務訪問 Redis 延時低,訪問鏈路不受機房間網路延時影響
業務單元化部署時,雙機房 Redis 會有較高的資料一致性
資料層雙向同步架構的缺點:
實現相對比較複雜,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 多活不會丟資料。
但這一代架構仍不夠完美,存在以下問題:
ProducerThread 把資料寫入 MQ 時,如果觸發 MQ 限流,資料會被丟掉
RedisServer 內部包含了 ProducerThread,當中間內部 queue 累積資料量超過10000條時,資料會被 MainThread 丟掉
中間同步資料寫入 MQ,增加了跨部門依賴,同步鏈路長,不利於系統穩定性
中間同步鏈路重試會造成非冪等命令執行多次,例如 incrby 重試可能造成命令執行多次造成資料不一致
對於新建雙活鏈路,不支援同步存量資料,只能從當前增量資料開始同步
Redis 增量資料寫入 MQ,導致成本增加
為了解決以上問題,我們開發了第三代架構。
第三代多活架構
在第三代架構中,我們細化了設計目標,主要思路是保證同步鏈路中的資料不丟不重,同時去掉對 MQ 的依賴,降低多活成本。
第三代架構中,我們去掉了 MQ 和 consumer,新增了 syncer 元件。syncer 元件模擬 Redis-slave 從 Redis-master 中拉取增量資料,這樣把資料同步和 Redis 進行解耦,便於後續多機房擴充套件。
在第三代架構中,Redis 遇到了迴環、重試、資料衝突、增量資料儲存和讀取等問題,接下來一一介紹我們應對這些問題的解決方案。
1、迴環問題
機房-A 寫入的資料同步到機房-B,防止資料再傳回機房-A。
為了解決迴環問題,我們開發了防迴環機制:
Redis 增加 shardID 配置,標識唯一分片號
Redis 請求中增加 opinfo,記錄元資訊,包含 shardID
機房-A 的 Proxy 寫入了 set k v 請求
機房-A 的 Redis-master 向 syncer 同步 set k v opinfo[shardID-1] 請求
syncer 向機房-B 寫入 set k v opinfo[shardID-1] 請求
這樣機房-B 根據 shardID-1 識別出這條請求是機房-A 生產的資料,因此不會再向機房-A 同步本條請求
2、重試問題
機房-A 寫入的 incrby 請求同步到機房-B,由於中間鏈路的重試,導致機房-B 可能執行了多次。
為了解決重試問題,我們開發了防重放機制:
Redis 增加 opid,標識唯一請求號
Redis 請求中增加 opinfo,記錄元資訊[opid]
機房-A 的 Proxy 寫入了 incrby k 1 請求
機房-A 的 Redis-master 向 syncer 同步了 incrby k 1 opinfo[opid=100] 請求, 之前同步的 opid=99 的請求已經成功
syncer 向機房-B 寫入 incrby k 1 opinfo[opid=100] 請求
機房-B 的 Redis 裡儲存了防重放資訊 shardID-1->opid[99]
機房-B 的 Redis 發現新請求的 opid=100>本地的99,判斷為新請求
機房-B的 Redis 執行這條請求,並把防重放資訊更新為shardID-1->opid[100]
假設機房-A 的 syncer 將本條請求進行了重試,又執行了一遍 incrby k 1 opinfo[opid=100]
機房-B 的 Redis 發現新請求 opid=100 等於本地的100,判斷為重複請求
機房-B 的 Redis 忽略掉本地請求,不執行
3、資料衝突問題
雙機房同時修改同一個 key 導致資料不一致
對於資料衝突,不同資料型別的不同操作的資料合併,如果單從儲存層解決,是一個非常複雜的話題。如果業務層做了單元化部署,則不會出現這種問題。如果業務層沒有做單元化,我們開發了衝突檢測功能,來幫助業務及時發現資料衝突,最後資料以哪邊為準來修正,需要業務同學來決策。
衝突檢測機制:
Redis 記錄 key 的最後 write 時間
Redis 請求中增加 opinfo,記錄元資訊 [timestamp]
如果 opinfo.timestamp<=key_write_time,則記錄衝突 key
時間T1<T2<T3
T1時間,使用者在機房-A 寫入請求 set k v1
T2時間,使用者在機房-B 寫入請求 set k v2,並記錄k的最後修改時間為T2
由於網路同步延時,T3時間,syncer 把T1時間寫入的 set k v1請求傳送到了機房-B
機房-B 的 Redis 執行 set k v1 時發現 timestamp 為T1,但 k 的最後修改時間為T2
由於T1<T2,機房-B 的 Redis 判斷這是一次衝突,並記錄下來,然後執行該條請求
以上是衝突檢測的基本原理,這是一個旁路統計,幫助使用者發現一些潛在衝突資料。
4、增量資料儲存和讀取問題
因為 syncer 只是同步元件,不會儲存資料,所以需要考慮當網路故障時,增量資料的儲存和讀取問題。
為了解決這個問題,我們對 Redis 的 aof 機制進行了改造,可以在網路故障時,增量資料都堆積在 Redis 的磁碟上,在網路恢復後,syncer 從 Redis 里拉取增量 aof 資料傳送到對端機房,避免資料丟失。
aof 機制改造有:aof 檔案切分、aof 增量複製、aof 非同步寫盤
將 aof 檔案切分為多個小檔案,儲存增量資料
當增量資料超過配置的閾值時,Redis 自動刪除最舊的 aof 檔案
當 Redis 重啟時,載入 rdb 檔案和 rdb 之後的 aof 檔案,可以恢復全部資料
當網路故障恢復後,syncer 根據故障前的 opid 向 Redis 請求拉取增量資料,傳送到對端機房
開源 Redis 是在主執行緒中進行 aof 寫盤,當磁碟 IO 過高時,Redis 寫盤可能造成業務訪問 Redis 耗時抖動。因此我們開發了 aof 非同步寫盤機制:
Redis 的主執行緒將 aof 資料寫入 queue 中
bio 執行緒來消費 queue
bio 執行緒將 aof 資料寫入磁碟
這樣 Redis 的訪問耗時不受磁碟 IO 的影響,更好的保證穩定性。
來自 “ 滴滴技術 ”, 原文作者:魏猛;原文連結:https://server.it168.com/a2023/1115/6829/000006829577.shtml,如有侵權,請聯絡管理員刪除。
相關文章
- Javascript模組化的演進歷程JavaScript
- 滴滴崩潰:異地多活形同虛設,疑似底層系統遭攻擊
- 異地多活和同城容災
- CnosDB有主複製演進歷程
- 想了解一個異地多校平臺的架構演進過程嗎? 讓我來告訴你!架構
- 阿里雲的“全站加速”技術演進歷程阿里
- B站Android程式碼庫的演進歷程Android
- Sermant在異地多活場景下的實踐
- 荔枝架構實踐與演進歷程架構
- 應用閘道器的演進歷程和分類
- 阿里巴巴在 Envoy Gateway 的演進歷程淺析阿里Gateway
- 分散式系統技術難題--異地多活分散式
- 滴滴 NewSQL 演進之 Fusion 實踐SQL
- Angular Universal 的演進歷史Angular
- 微服務18:微服務治理之異地多活容災微服務
- 大型分散式架構的演進歷史(前方多圖告警)分散式架構
- 滴滴資料通道服務演進之路
- 異地多活的資料一致性簡單設計
- 一文讀懂資料平臺建設的演進歷程
- mssql資料庫異地進行異地備份的方法SQL資料庫
- 微服務的架構演進過程和多個解決方案微服務架構
- 分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路分散式SQL資料庫
- 滴滴機器學習平臺架構演進機器學習架構
- 架構系列---餓了麼MySQL異地多活的資料雙向複製架構MySql
- 滴滴機器學習平臺架構演進之路機器學習架構
- 從github超24小時的故障看異地多活全域性設計的重要性Github
- Java分散式架構的演進過程Java分散式架構
- 從“挖光纜”到“剪網線”|螞蟻金服異地多活的微服務體系微服務
- redis系列--主從複製以及redis複製演進Redis
- 我經歷過的監控系統演進史
- 滴滴出行登陸紐西蘭 當地80%的網約車活躍司機已在滴滴平臺註冊
- 《六週玩轉雲原生》- 微服務架構下服務治理體系的演進歷程?微服務架構
- 淘寶10年的架構演進過程架構
- 圖解分散式架構的演進過程!圖解分散式架構
- 內部專家親自揭秘!滴滴物件儲存系統的演進之路物件
- 最新進展!谷歌AutoML-Zero驗證自我演化:成功復現數十年AI演進歷程谷歌TOMLAI
- 軟體自動化測試工具的歷史演進
- 淺談儲存器的進化歷程