1. 程式人生 > 其它 >百億級資料的實時存取優化與實踐

百億級資料的實時存取優化與實踐

極光筆記丨百億級資料的實時存取優化與實踐 https://mp.weixin.qq.com/s/PCcms6Rs_B7z5InzZdvxCQ

摘要

極光推送後臺標籤/別名系統儲存超過百億條資料, 高峰期QPS超過50萬, 且隨著業務的發展,儲存容量和訪問量還在不斷增加。之前系統存在的一些瓶頸也逐漸顯現,所以近一兩年持續做了很多的優化工作,最終達到非常不錯的效果。近期,經過積累和沉澱後,將這一部分的工作進行總結。

01

背景

當前的舊系統中主要儲存標籤/別名與註冊ID的相互對映關係, 使用Key-Value結構儲存, 考慮到一個註冊ID可能有多個標籤, 同時一個標籤也存在多個不同的註冊ID, 這部分資料使用Redis儲存中的Set資料結構; 而一個註冊ID只有一個別名, 同時一個別名也存在多個不同的註冊ID, 這部分資料使用String/Set資料結構。由於此部分資料量過大, 考慮到儲存成本, Redis使用的單Master模式, 而最終資料的落地使用Pika儲存(一種類Redis的檔案儲存)。Pika與Redis中的資料由業務方保持一致, Redis正常可用時, 讀Redis; 當Redis不可用時讀Pika, Redis恢復可用後, 從Pika恢復資料到Redis, 重新讀Redis. 舊系統的儲存架構如下:

從上面的架構圖可以看到, Redis/Pika均採用主從模式, 其中Redis只有Master, 配置管理模組用來維護Redis/Pika叢集的主從關係, 配置寫入ZooKeeper中, 業務模組從ZooKeeper中讀取配置, 不做配置變更。所有的配置變更由配置管理模組負責。當需要遷移, 擴容, 縮容的時候, 必須通過配置管理模組操作. 這個舊系統的優缺點如下:

優點:

  • 配置集中管理, 業務模組不需要分開單獨配置

  • 讀取Redis中資料, 保證了高併發查詢效率

  • Pika 主從模式, 保證了資料落地, 不丟失

  • 配置管理模組維護分片slot與例項的對映關係, 根據Key的slot值路由到指定的例項

缺點:

  • Redis與Pika中儲存的資料結構不一致, 增加了程式碼複雜度

  • Redis單Master模式, Redis某個節點不可用時, 讀請求穿透到Pika, 而Pika不能保證查詢效率, 會造成讀請求耗時增加甚至超時

  • Redis故障恢復後, 需要從Pika重新同步資料, 增加了系統不可用持續時間, 且資料一致性需要更加複雜的計算來保證

  • 當遷移/擴容/縮容時需要手動操作配置管理模組, 步驟繁瑣且容易出錯

  • Redis中儲存了與Pika同樣多的資料, 佔用了大量的記憶體儲存空間, 資源成本很高

  • 整個系統的可用性還有提升空間, 故障恢復時間可以儘量縮短

  • 配置管理模組為單點, 非高可用, 當此服務down掉時, 整個叢集不是高可用, 無法感知Redis/Pika的心跳狀態

  • 超大Key打散操作需要手動觸發. 系統中存在個別標籤下的註冊ID過多, 儲存在同一個例項上, 容易超過例項的儲存上限, 而且單個例項限制了該Key的讀效能

02

舊系統缺點分析

考慮到舊系統存在以上的缺點, 主要從以下幾個方向解決:

Redis與Pika中儲存的資料結構不一致, 增加了程式碼複雜度

分析: 舊系統中Redis與Pika資料不一致主要是Pika早期版本Set資料結構操作效率不高, String資料結構操作的效率比較高, 但獲取標籤/別名下的所有註冊ID時需要遍歷所有Pika例項, 這個操作非常耗時, 考慮到最新版本Pika已經優化Set資料結構, 提高了Set資料結構的讀寫效能, 應該保持Redis與Pika資料結構的一致性。

Redis單Master模式, Redis某個節點不可用時, 讀請求穿透到Pika, 而Pika不能保證查詢效率, 會造成讀請求耗時增加甚至超時

分析: Redis單Master模式風險極大。需要優化為主從模式, 這樣能夠在某個Master故障時能夠進行主從切換, 不再從Pika中恢復資料, 減少故障恢復時間, 減少資料不一致的可能性。

Redis故障恢復後, 需要從Pika重新同步資料, 增加了系統不可用持續時間, 且資料一致性需要更加複雜的計算來保證

分析: 這個系統恢復時間過長是由於Redis是單Master模式, 且沒有持久化, 需要把Redis優化成主從模式且必須開啟持久化, 從而幾乎不需要從Pika恢復資料了, 除非Redis的主從例項全部同時不可用。不需要從Pika恢復資料後, 那麼Redis中的資料在Redis主從例項發生故障時, 就和Pika中的資料一致了。

當遷移/擴容/縮容時需要手動操作配置管理模組, 步驟繁瑣且容易出錯

分析: 配置管理模組手動干預操作過多, 非常容易出錯, 這部分應儘量減少手動操作, 考慮引入Redis哨兵, 能夠替換大部分的手動操作步驟。

Redis中儲存了與Pika同樣多的資料, 佔用了大量的記憶體儲存空間, 資源成本很高

分析: 通過對Redis中的各個不同維度資料進行資料量和訪問量以及訪問來源分析(如下圖)。外部請求量(估算) 這欄的資料反應了各個不同Key的單位時間內訪問量情況。

Redis的儲存資料主要分為標籤/別名到註冊ID和註冊ID到標籤/別名兩部分資料. 通過分析得知, 標籤/別名到註冊ID的資料約佔1/3左右的儲存空間, 訪問量佔到80%; 註冊ID到標籤/別名的資料約佔2/3左右的儲存空間, 訪問量佔到20%。可以看到, 紅色數字部分為訪問的Pika, 黑色部分訪問的Redis, 3.7%這項的資料可以優化成訪問Redis, 那麼可以得出結論, 紅色的資料在Redis中是永遠訪問不到的。所以可以考慮將Redis中註冊ID到標籤/別名這部分資料刪掉, 訪問此部分資料請求到Pika, 能夠節省約2/3的儲存空間, 同時還能保證整個系統的讀效能。

整個系統的可用性還有提升空間, 故障恢復時間可以儘量縮短

分析: 這部分主要由於其中一項服務為非高可用, 而且整個系統架構的複雜性較高, 以及資料一致性相對比較難保證, 導致故障恢復時間長, 考慮應將所有服務均優化為高可用, 同時簡化整個系統的架構。

配置管理模組為單點, 非高可用, 當此服務down掉時, 整個叢集不是高可用, 無法感知Redis/Pika的心跳狀態

分析: 配置手動管理風險也非常大, Pika主從關係通過配置檔案手動指定, 配錯後將導致資料錯亂, 產生髒資料. 需要使用Redis哨兵模式, 用哨兵管理Redis/Pika, 配置管理模組不再直接管理所有Redis/Pika節點, 而是通過哨兵管理, 同時再發生主從切換或者節點故障時通知配置管理模組, 自動更新配置到Zookeeper中, 遷移/擴容/縮容時也基本不用手動干預。

超大Key打散操作需要手動觸發。系統中存在個別標籤下的註冊ID過多, 儲存在同一個例項上, 容易超過例項的儲存上限, 而且單個例項限制了該Key的讀效能

分析: 這部分手動操作, 應該優化為自動觸發, 自動完成遷移, 減少人工干預, 節省人力成本。

03

Redis哨兵模式

Redis哨兵為Redis/Pika提供了高可用性, 可以在無需人工干預的情況下抵抗某些型別的故障, 還支援監視, 通知, 自動故障轉移, 配置管理等功能:

  • 監視: 哨兵會不斷檢查主例項和從例項是否按預期工作

  • 通知: 哨兵可以將出現問題的例項以Redis的Pub/Sub方式通知到應用程式

  • 自動故障轉移: 如果主例項出現問題, 可以啟動故障轉移, 將其中一個從例項升級為主, 並將其他從例項重新配置為新主例項的從例項, 並通知應用程式要使用新的主例項

  • 配置管理: 建立新的從例項或者主例項不可用時等都會通知給應用程式

同時, 哨兵還具有分散式性質, 哨兵本身被設計為可以多個哨兵程序協同工作, 當多個哨兵就給定的主機不再可用這一事實達成共識時, 將執行故障檢測, 這降低了誤報的可能性。即使不是所有的哨兵程序都在工作, 哨兵仍能正常工作, 從而使系統能夠應對故障。

Redis哨兵+主從模式能夠在Redis/Pika發生故障時及時反饋例項的健康狀態, 並在必要時進行自動主從切換, 同時會通過Redis的sub/pub機制通知到訂閱此訊息的應用程式。從而應用程式感知這個主從切換, 能夠短時間將連結切換到健康的例項, 保障服務的正常執行。

沒有采用Redis的叢集模式, 主要有以下幾點原因:

  • 當前的儲存規模較大, 叢集模式在故障時, 恢復時間可能很長

  • 叢集模式的主從複製通過非同步方式, 故障恢復期間不保證資料的一致性

  • 叢集模式中從例項不能對外提供查詢, 只是主例項的備份

  • 無法全域性模糊匹配Key, 即無法遍歷所有例項來查詢一個模糊匹配的Key

04

最終解決方案

綜上, 為了保證整個儲存叢集的高可用, 減少故障恢復的時間, 甚至做到故障時對部分業務零影響, 我們採用了Redis哨兵+Redis/Pika主從的模式, Redis哨兵保證整個儲存叢集的高可用, Redis主從用來提供查詢標籤/別名到註冊ID的資料, Pika主從用來儲存全量資料和一些註冊ID到標籤/別名資料的查詢。需要業務保證所有Redis與Pika資料的全量同步。新方案架構如下:

從上面架構圖來看, 當前Redis/Pika都是多主從模式, 同時分別由不同的多個哨兵服務監視, 只要主從例項中任一個例項可用, 整個叢集就是可用的。Redis/Pika叢集內包含多個主從例項, 由業務方根據Key計算slot值, 每個slot根據配置管理模組指定的slot與例項對映關係。如果某個slot對應的Redis主從例項均不可用, 會查詢對應的Pika, 這樣就保證整個系統讀請求的高可用。這個新方案的優缺點如下:

優點:

  • 整個系統所有服務, 所有儲存元件均為高可用, 整個系統可用性非常高

  • 故障恢復時間快, 理論上當有Redis/Pika主例項故障, 只會影響寫入請求, 故障時間是哨兵檢測的間隔時間; 當Redis/Pika從例項故障, 讀寫請求都不受影響, 讀服務可以自動切換到主例項, 故障時間為零, 當從例項恢復後自動切換回從例項

  • 標籤/別名儲存隔離, 讀寫隔離, 不同業務隔離, 做到互不干擾, 根據不同場景擴縮容

  • 減少Redis的記憶體佔用2/3左右, 只使用原有儲存空間的1/3, 減少資源成本

  • 配置管理模組高可用, 其中一個服務down掉, 不影響整個叢集的高可用, 只要其中一臺服務可用, 那麼整個系統就是可用

  • 可以平滑遷移/擴容/縮容, 可以做到遷移時無需業務方操作, 無需手動干預, 自動完成; 擴容/縮容時運維同步好資料, 修改配置管理模組配置然後重啟服務即可

  • 超大Key打散操作自動觸發, 整個操作對業務方無感知, 減少運維成本

缺點:

  • Redis主從例項均可不用時, 整個系統寫入這個例項對應slot的資料均失敗, 考慮到從Pika實時同步Redis資料的難度, 並且主從例項均不可用的概率非常低, 選擇容忍這種情況

  • 哨兵管理增加系統了的複雜度, 當Redis/Pika例項主從切換時通知業務模組處理容易出錯, 這部分功能已經過嚴格的測試以及線上長時間的功能檢驗

05

其他優化

除了通過以上架構優化, 本次優化還包括以下方面:

  • 通過IO複用, 由原來的每個執行緒一個例項連結, 改為在同一個執行緒同時管理多個連結,提高了服務的QPS, 降低高峰期的資源使用率(如負載等)

  • 之前的舊系統存在多個模組互相呼叫情況, 減少模組間耦合, 減少部署及運維成本

  • 之前的舊系統模組間使用MQ互動, 效率較低, 改為RPC呼叫, 提高了呼叫效率, 保證呼叫的成功率, 減少資料不一致, 方便定位問題

  • 不再使用Redis/Pika定製化版本, 可根據需要, 升級到Redis/Pika官方的最新穩定版本

  • 查詢模組在查詢大Key時增加快取, 快取查詢結果, 此快取過期前不再查詢Redis/Pika, 提高了查詢效率

06

展望

未來此係統還可以從以下幾個方面繼續改進和優化:

  • 大Key儲存狀態更加智慧化管理, 增加設定時的大Key自動化遷移, 使儲存更加均衡

  • 制定合理的Redis資料過期機制, 降低Redis的儲存量, 減少服務儲存成本

  • 增加集合操作服務, 實現跨Redis/Pika例項的交集/並集等操作, 並新增快取機制, 提高上游服務訪問效率, 提高推送訊息下發效率

07

總結

本次系統優化在原有儲存元件的基礎上, 根據服務和資料的特點, 合理優化服務間呼叫方式, 優化資料儲存的空間, 將Redis當作快取, 只儲存訪問量較大的資料, 降低了資源使用率。Pika作為資料落地並承載訪問量較小的請求, 根據不同儲存元件的優缺點, 合理分配請求方式。同時將所有服務設計為高可用, 提高了系統可用性, 穩定性。最後通過增加快取等設計, 提高了高峰期的查詢QPS, 在不擴容的前提下, 保證系統高峰期的響應速度。