跨雲平臺大數據系統遷移實踐

背景介紹

最近又能擠一擠時間,來聊一聊前一段時間接手的一個大數據系統項目。

隨着雲計算的普及,大部分互聯網公司的系統都是基於雲原生的產品和體系來搭建的,我接手的系統也不例外。數據處理部分從底層存儲,到中間層數據處理系統,再到上層的ETL系統第一版都是基於Google Cloud Platform來搭建的,GKE + PubSub + DataFlow + FireStore,數據服務部分,負載均衡也是Google App Engine的體系。

GCP的產品在接入的環節做的都很便捷,門檻低,對應產品主流開發語言的SDK一應俱全,在項目排期緊張的情況下,整套系統架構基於GCP進行開發、部署與使用都很方便。

那爲什麼要做一次遷移呢?經過這段時間的折騰,回頭想想主要原因有以下幾點:

  1. 我們主要的服務對象發生了變化,從海外用戶逐漸迴歸到國內用戶
  2. 我們數據產品對實時性有要求,國內用戶查詢的延遲不能超過1s,增量數據的更新要實時(不超過3s)
  3. GCP整體的成本太高,特別是美元匯率,再加上和國內阿里雲相比,遷移後能省不少費用

那麼我們在遷移中遇到了什麼挑戰呢?我認爲最有挑戰性的有兩塊:雲計算圈子有一句話是存儲在哪裏,用戶就在哪裏,由此可見存儲做的好對用戶的粘性有多大了吧。那遇到的最大的挑戰就是整體存儲的遷移,從原來的Firestore遷移到mongodb,批處理數據導入hbase;其次是整套數據處理框架的改造,由原來的Apache beam改爲Flink framework。

接下來結合遷移計劃,我將對v1與v2兩個版本的大數據系統做更詳細的介紹與分析。

###遷移計劃

  • 數據雙寫,流處理的數據同時寫入mongodb,批處理的部分寫入hbase
  • API層重構,不同存儲的client +和遷移數據進行合併
  • firestore到mongo的遷移
  • 流量切換
  • firestore數據清理

v1數據系統

系統架構

第一期系統架構

從架構圖可以看出ETL的鏈路比較長,從源數據隊列消費後需要上傳一次阿里雲OSS,再依賴OSS callback功能觸發GCP上的pipeline程序進行GetObject的操作,最後再把數據導入GCP上的ETL系統做數據清洗、處理和聚合入庫。

爲什麼這麼選型+技術點

團隊歷史原因導致的整體鏈路冗長,這裏忽略1w字。存儲的選型上,我們選擇了Firestore,這裏firestore可能對國內的同學比較陌生,其實可以當其爲分佈式分片存儲的mongodb集羣,但是Firestore的subcollection功能特別符合我們的場景,我們的數據是基於用戶id存儲的document,subcollection可以讓我們方便的在doc內創建子collection,這樣就很容易基於某個用戶id做用戶維度下的二級索引,做用戶維度下的二級甚至N級維度的數據聚合。

數據處理模型上我們選用是Dataflow進行流處理,因爲我們的使用場景裏,一個時間窗口內不會有重複的用戶id出現,所以處理方式是獲取到數據後,做1次IO+中間值處理。中間的operator也比較簡單,第一版的設計只是依賴Dataflow的worker管理能力,我們的業務層邏輯只有一個worker,因此整體的邏輯處理模型如下:

數據處理v1

整個項目上線10天左右的時間,數據處理層在沒有過多優化的情況下,在流量高峯時是要做橫向擴容才能保證處理速度的。那在第一版裏最大的挑戰是在於整個鏈路如何保障速度和穩定性,特別是在跨洋處理的這一段。

我們做了以下幾個處理:

  1. 源數據上傳OSS,做了重試與冷啓動邏輯,海外鏈路波動會經常導致上傳OSS失敗,這意味着沒有觸發oss的callback。源數據會定時重試,清理本地失敗文件夾裏的數據文件。
  2. OSS選擇在HK節點,這麼做對國內和海外的鏈路都比較適中,這裏如果直接選擇國內的OSS,網絡波動很不穩定,相比HK的網絡波動基本是在國內下午的時間會有一段峯值,和網絡超時,其餘時間都還算穩定,這樣保證我們的數據傳輸在3-5s內是可以完成
  3. GCP上Pipeline做了數據拉取與二次推送的跟蹤,如果拉取數據及推送PubSub失敗都加上了重試和冷卻的邏輯

數據上傳網絡監控

v2數據系統

遷移後的數據系統,主要的變化有三個:

  • 沒有海外鏈路的部分,數據都在阿里雲內網處理
  • 數據處理層拆分成流處理與批處理兩個模型,從Dataflow改爲Flink集羣
  • 數據存儲從Firestore改爲MongoDB與HBase,原始數據存在oss上

存儲選型的一些思考:

  1. Firestore之前提到與MongoDB類似,所以遷移的選型在考慮數據結構與模型沒有太大變化的情況下,所以選擇Mongo的分片集羣。

一般使用Shard Cluster MongoDB, 主要考慮幾個因素:

  • 數據容量擴展性
  • 讀寫性能擴展性

設計shard key主要目標有:

  • key 分佈足夠離散 (sufficient cardinality)
  • 寫請求均勻分佈 (evenly distributed write)
  • 儘量避免 scatter-gather 查詢 (targeted read)

Mongo分片集羣架構

一般通過document裏的某個字段作hash來完成sharding,這裏需要考慮到jumbo chunk的問題。如果只是基於某個字段進行分片,邏輯中再根據時間進行query,這樣還是會導致基於某個字段的數據都集中在一個分片中,因此根據請求和業務邏輯,可以通過(key1, key2, key3…)這樣的方式進行shard key設計。

chunk達到size之後會分裂,除了jumbo chunk的情況。底層的balancer程序會根據shard的情況遷移chunk。具體mongo分片的邏輯可以參考https://docs.mongodb.com/manual/core/sharding-shard-key/

  1. 根據數據的訪問頻次和處理方式,我們還選擇了HBase用於批處理的數據存儲,按用戶一天、一週與一個月的數據進行聚合;OSS存儲原始數據考慮到部分數據並非熱點數據,並且是原始數據格式的format後直接顯示,所以不用轉存一份,這裏我們是使用MongoDB來做二級索引,比如某個用戶ID下的所有Match ID。另外OSS上的數據也是按天存儲,批處理模型也直接是從OSS讀數據作爲datasource進行批處理計算。

  2. 那麼之前Firestore SubCollection的數據怎麼處理?

  • 基於MongoDB沒有subcollection的特性,這裏我們是將subcollection的數據通過User ID與維度ID做的sharding key,新創建一個維度表進行存儲

流處理部分

流處理Flink DAG

V1的架構中,我們的處理模型是一個wokrer處理所有的數據在分別寫Firestore collection裏的documents。

在V2的架構裏,我們從Dataflow遷移到了Flink的集羣,所以在operator的拆分上也做了一些優化,即讀取原始數據後寫入不同的flatmap算子給到不用維度的數據使用,在分別聚合和sink。

這麼做的好處是我們在基於flink自身check points和自己做savepoints時可以更細粒度的做恢復,且之前dataflow裏如果要更新集羣就需要drain dataflow,屏蔽source讀取,把已有的數據處理完,再開啓新的集羣處理新的數據。

遷移與刪除的容錯與優化

搭建完系統後,兩套系統同時在線進行雙寫,之後再開始數據遷移的工作,這裏我們的做法還是覆蓋overview數據並記錄遷移時間,在API層通過全局的overview已聚合數據再聚合遷移時間之後產生的數據。

所以遷移過程沒有那麼複雜,需要注意的是遷移過程因爲也是跨洋遷移,做了以下幾點優化:

  1. 爲了防止網絡超時出現數據缺失,mongo的endpoint是通過香港節點作爲4層代理進行寫入
  2. 遷移程序同樣需要retry和冷卻邏輯,這裏記得retry成功後需要更新遷移時間

踩了什麼大坑(持續更新)

  1. Firestore數據的刪除
  • Firestore寫入和查詢用起來都很方便,但是在數據刪除上坑很大,第一是沒有console能一鍵刪除整個db;第二是console上提供了刪除collectin的功能,但是實現的邏輯是遍歷collection的doc一個個刪除,還要保持在線,穩定性差,耗時也很長;第三刪除之後的doc index還會存在console上,因此無法判斷是刪除成功還是失敗。

是實現的邏輯是遍歷collection的doc一個個刪除,還要保持在線,穩定性差,耗時也很長;第三刪除之後的doc index還會存在console上,因此無法判斷是刪除成功還是失敗。

  1. 跨洋鏈路不太穩定,建議是所有的跨洋通信都需要加上hk節點的代理,重試和冷卻邏輯也必不可少。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章