在 Spark 資料匯入中的一些實踐細節
本文由合合資訊大資料團隊柳佳浩撰寫
1.前言
圖譜業務隨著時間的推移愈發的複雜化,逐漸體現出了效能上的瓶頸:單機不足以支援更大的圖譜。然而,從效能上來看,Neo4j 的原生圖儲存有著不可替代的效能優勢,這一點是之前調研的 JanusGraph、Dgraph 等都難以逾越的鴻溝。即使 JanusGraph 在 OLAP 上面非常出色,對 OLTP 也有一定的支援,但是 GraphFrame 等也足以支撐其 OLAP 需求,更何況在 Spark 3.0 會提供 Cypher 支援的情況下,圖譜的 OLAP 需求相比 OLTP 有更多途徑可以解決。這個時候,Nebula Graph 的“橫空出世”無疑是對分散式 OLTP 效率低下現狀的一種突破。
之前在各類調研、部署後,特別是從 JanusGraph 的 OLTP 效率最終測試發現無法滿足線上需求之後,我們不再對同一圖譜可以同時進行 OLAP 和 OLTP 進行強制性要求,而 Nebula Graph 的架構剛好符合圖譜方面的需要:
- 分散式——shared-nothing 分散式架構
- 高速 OLTP(效能需要和 Neo4j 相近)——Nebula Graph 的儲存層架構查詢直接對映實體地址,實際上可以算是原生圖儲存
- 服務的高可用(即在非人為情況下,圖譜可以穩定提供服務)——區域性失敗服務可用、有快照機制
- 保證可擴充套件性——支援線性擴容,由於開源、支援二次開發
綜上所述,Nebula Graph 架構上符合實際生產需求,因此對 Nebula Graph 進行了調研、部署、測試。關於部署、效能測試(
2.測試環境
- Nebula Graph 叢集
- 3 臺 32 c(實際限制了16 c)
- 400 G 記憶體(實際配置了 100 G)
- SSD
- 版本資訊:Nebula Graph 版本 1.0.0(當時測試比較早)。
- 網路環境:萬兆。
- 圖譜大小:十億級別節點(屬性較少),百億級別邊(有向,無屬性或帶權值)。
- Spark 叢集
- 版本資訊:Spark 2.1.0
實際上 Nebula Graph 的使用資源合計 2T 左右 memory (3 * 30 executor + 1 driver) * 25G。
3.Spark 批量匯入
3.1 基礎流程
- 打包 sst.generator(Spark 生成 sst 所需要的包)。
- 配置 Nebula Graph 叢集,Nebula Graph 叢集正常啟動,建立圖譜。
- Spark 配置檔案
config.conf
(可以參考文件《Spark 匯入工具》)進行配置。 - 排查 Spark 叢集是否存在衝突的包。
- Spark 啟動時使用配置檔案和
sst.generator
快樂地匯入。 - 資料校驗。
3.2 一些細節
- 批量匯入前推薦先建立索引。
這裡推薦先建立索引的原因是:批量匯入僅在非線上圖譜進行,雖然建立索引可以選擇是否在提供服務的同時進行,但是為了防止後續 REBUILD
出現問題,這邊可以優先建好索引。帶來的問題就是在批量匯入結點時相對較慢。
-
推薦用 int 型節點 ID(可以使用 Snowflake演算法 等),如果節點的 ID 不是 int 型,這裡可以通過在節點/邊中加入
policy: "uuid"
來設定自動生成 uuid。 -
如果使用的是單獨的 Spark 叢集可能不會出現 Spark 叢集有衝突包的問題,該問題主要是 sst.generator 中存在可能和 Spark 環境內的其他包產生衝突,解決方法是 shade 掉這些衝突的包,或者改名。
-
Spark 調優方面:可以根據實際情況調整引數,儘量降低 memory 以節約資源,相對的可以適當提高並行度加速。
3.3 匯入結果
十億級別節點(屬性較少),百億級別邊(有向,無屬性或帶權值),提前建好索引的情況下大約消耗 20 小時左右匯入全圖。
3.4 關於 PR
因為在較早的版本使用了 Spark 匯入,自然也有一些不太完善的地方,這邊也提出了一些拙見,對 SparkClientGenerator.scala 略作了修改。
- 最早在使用 Spark Writer(現:Exchange) 寫入 Nebula Graph 時,發現錯列的問題。
通過看原始碼發現 SparkClientGenerator.scala 存在 BUG,讀取的是配置檔案的位置而非 parquet/json
檔案的位置,修復後提了我第一個 PR#2187,有幸通過
- 後續發現使用 SparkClientGenerator 自動生成 uuid/hash 功能時,存在會出現重複的雙引號的問題,導致無法匯入。
這塊可以說是由於解決問題的想法不同,提交了好多次。重複引號的問題歸根結底是對型別轉化的時候添加了一次雙引號,我這邊發現有個 extraIndexValue 的方法可以把使用者自填的非 string 型別的轉成 string 型別,我這邊想著可能會有使用者想把非 string 型的 index 轉成 uuid/hash(比如 array),所以修改的比較多。
但是和官方 @darionyaphet 溝通後,發現我這種做法其實是對資料來源進行了修改,使用者傳 array 等不支援的型別時,應該報錯而不是轉換型別(這個確實,一開始只考慮到了邏輯上跑通以及自己這邊業務的使用,沒考慮通用性)。重新修改,提交 PR #2258,通過。經過這次 PR 我也學到了很多。
- 之後發現 nebula-python 也有和官方 thrift 衝突的問題,本來想 shade 後提 PR,但是覺得這個改動太大了,所以直接提給官方,近期也修復了。
Nebula Graph 旁白:歡迎社群小夥伴來 GitHub 給我們提 PR,GitHub 傳送門:https://github.com/vesoft-inc/nebula/issues
4.總結 & 展望
因為之前調研過 JanusGraph,Nebula Graph 給我的第一印象就是:暗坑相對較少、社群反饋非常及時。在測試後 Nebula Graph 又用她的效率證明了自己,成為了分散式圖譜的首選項。
Nebula Graph 社群、群組、PR 官方反饋非常及時,這是圖譜迅速、茁壯成長的不可替代的重要因素,也希望可以後續可以繼續見證 Nebula Graph 的成長,繼續為 Nebula Graph 生態的完善添磚加瓦!
喜歡這篇文章?來來來,給我們的 GitHub 點個 star 表鼓勵啦~~