開源分佈式圖數據庫的思考和實踐

本文首發於 Nebula Graph Community 公眾號

本文整理自 DTCC 主題演講【開源分佈式圖數據庫的思考和實踐】

目錄

目錄

  • 圖數據庫市場的現狀
  • 圖數據庫的優勢
  • 以 Nebula Graph 為例
  • 開源社區

圖數據庫市場的現狀

開篇之前,先回顧下圖數據庫市場變化,2018 年前市場大概是 $ 650,000,000,根據目前市場的研究報告,圖數據庫市場將在未來的 4 年以年增長 30%~100% 的速度達到 $ 4,130,000,000~$ 8,000,000,000。

DTCC2021

而整個圖數據庫市場的發展,從公開資料上來看,Graph Database(圖數據庫) 概念最早是 Gartner 在 2013 年提出的。而當時比較重要的 2 個參與者是——Neo4j 和 TitanDB,後者被收購後誕生了 JanusGraph。一般來說一個事物發展到一定階段會開始走下坡路,不過圖領域目前來說還是處於蓬勃發展階段,在 2021 年 Gartner 的數據和分析十大趨勢中,圖還是佔有一席之地。Graph relate everything,相比傳統數據庫,它的拓展範圍更大,包括:圖計算、圖處理、圖深度學習、圖機器模型等等。

DTCC2021

下面這張圖是做數據庫排名的 DB-Engine 給出的數據庫增長趨勢,可以見到圖數據庫是近 10 年來關注度增長最快的數據庫。表的數據來源於社交媒體,例如:LinkedIn、Twitter 提及某個技術點的次數,以及 Stack Overflow 上面相關問題數、搜索引擎搜索趨勢。

DTCC2021

圖數據庫的優勢

一般來說,圖相對別的數據庫,最明顯的優勢便是直觀。下面是一個權力遊戲人物關係的表結構圖和圖結構圖:

DTCC2021
(表結構)

DTCC2021
(圖結構)

還有一個是表達簡潔,在同樣的數據模型(下圖)

DTCC2021

實現:找一定的時間範圍內,總共發了多少 posts,各自被回復多少次並對結果進行排序,SQL 的編寫可能是這樣的:

--PostgreSQL 

WITH RECURSIVE post_all (psa_threadid

                      , psa_thread_creatorid, psa_messageid

                      , psa_creationdate, psa_messagetype

                       ) AS (

    SELECT m_messageid AS psa_threadid

         , m_creatorid AS psa_thread_creatorid

         , m_messageid AS psa_messageid

         , m_creationdate, 'Post'

      FROM message

     WHERE 1=1 AND m_c_replyof IS NULL -- post, not comment

       AND m_creationdate BETWEEN :startDate AND :endDate

  UNION ALL

    SELECT psa.psa_threadid AS psa_threadid

         , psa.psa_thread_creatorid AS psa_thread_creatorid

         , m_messageid, m_creationdate, 'Comment'

      FROM message p, post_all psa

     WHERE 1=1 AND p.m_c_replyof = psa.psa_messageid

     AND m_creationdate BETWEEN :startDate AND :endDate

)

SELECT p.p_personid AS "person.id"

     , p.p_firstname AS "person.firstName"

     , p.p_lastname AS "person.lastName"

     , count(DISTINCT psa.psa_threadid) AS threadCount

END) AS messageCount

     , count(DISTINCT psa.psa_messageid) AS messageCount

  FROM person p left join post_all psa on (

       1=1   AND p.p_personid = psa.psa_thread_creatorid

   AND psa_creationdate BETWEEN :startDate AND :endDate

   )

 GROUP BY p.p_personid, p.p_firstname, p.p_lastname

 ORDER BY messageCount DESC, p.p_personid

 LIMIT 100;

用 Cypher(圖查詢語言)表達的話,僅用下面語句即可:

--Cypher
MATCH (person:Person)<-[:HAS_CREATOR]-(post:Post)<-[:REPLY_OF*0..]-(reply:Message)
WHERE  post.creationDate >= $startDate   AND  post.creationDate <= $endDate
  AND reply.creationDate >= $startDate   AND reply.creationDate <= $endDate
person. RETURN
id,   person.firstName,   person.lastName,   count(DISTINCT post) AS threadCount,
  count(DISTINCT reply) AS messageCount
ORDER BY
  messageCount DESC,  person.id ASC
LIMIT 100

在查詢這塊,圖查詢的表達方式更簡潔。

此外,圖本身的生態也相當豐富,下面為 2020 年的圖技術全景圖,而在 2021 年新增的技術點會更多。

DTCC2021

以 Nebula Graph 為例

下面以 Nebula 為例,介紹下分佈式圖數據庫 Nebula Graph 特性,以及開發過程中遇到何種技術挑戰、研發團隊又是如何處理這種技術挑戰。

在 19 年 5 月底開源之前,18 年年底開始設計 Nebula Graph 雛形時,研發團隊定了 4 個目標:規模、生產、OLTP、開源生態,而這 4 個目標直至今日依舊影響 Nebula Graph 產品規劃。

Nebula Graph 設計的第一個要點是:規模,同其他競品設計初衷不大一樣,Nebula Graph 一開始設計時,考慮到數據庫未來處理的數據規模一定會很大——摩爾定理總是快不過數據增長,而單機情況並不能很好地應對海量數據以及未來的數據增長,繼而轉向研究分佈式數據庫是如何處理數據。所以說,Nebula Graph 的設計是針對生產工業級別的數據量:萬億點邊數據量。在數據規模大的情況下,整個圖數據分析屬性得多,不同於另外一種只做圖結構、不做圖屬性的圖數據庫設計,Nebula Graph 開始的設計便是:大規模的屬性圖,支持上百屬性,用戶可在具體場景上落地 Nebula Graph 實踐。

第二點是生產,除了第一點提到的工業級別可用之外,還有查詢語言如何設計、可視化、可編程、運維要求。

第三點是 OLTP,當時的設計目標優先考慮 TP 場景,即使到今天也是一樣,Nebula Graph 是一個非常關注 TP 場景的圖數據庫,即:它是一個在線、高並發、低延遲的圖數據庫。一般來說互聯網的業務場景對這塊的需求比較多,比如說你的一筆轉賬交易支付時,系統可能要檢測一下是否安全,這個處理時間可能是幾十毫秒左右。

最後一點開源,除了構建技術社區、開發者生態之外,主要還考慮了對接其他大數據生態,以及同圖計算、訓練框架進行結合。

DTCC2021

這是目前 Nebula Graph 的生態圖,紅色為內核部分,分為 meta、graph、storage 三個模塊,下文會詳細講解。內核部分上層為查詢語言,除了自研的 nGQL 之外,還兼容了 openCypher。再上層為客戶端,目前支持 Java、C++、Python、Go 等等語言。而客戶端上層為可編程 SDK,最上層為大數據生態支持層,比如常見的 Spark、Flink,圖計算 GraphX、Plato,還有其他數據源遷移工具。左側為雲部署、可視化、監控模塊,右側主要側重數據安全,比如說:數據備份、多機房部署…,還有同工程相關的 Chaos 以及性能壓測,最右側是社區和開發者相關的內容。

DTCC2021

來詳細講述下 Nebula 核心設計,主要包括 meta、graph、storage,右上角的 meta 主要是元數據,中間的 graph 是查詢引擎部分,下面的 storage 是存儲引擎,這三個模塊進程分離,存儲計算分離的設計也體現了之前說到的 Nebula Graph 設計之初考慮規模大這一點。

詳細地來說,元數據模塊(Meta Service)主要用於管理 Schema,Nebula Graph 的 Schema 不是完全 Schema freee 的設計,主要體現為點和邊的屬性是需要預先定義。此外,space 管理、權限管理、長耗時任務管理、數據清理等等管理都是在 meta 模塊進行。在具體使用中,meta 一般部署 3 個節點,這 3 個節點進行強同步。存儲引擎層 Storage Service 是多進程系統,多個進程之間做一個強同步

上文說到 Nebula 支持萬億規模的點邊數據量,肯定要對圖切片。一般來說圖切片分為兩種:切點切邊。Nebula Graph 的設計是切邊,將點放在 Partition 上,邊的出邊和入邊也放在各自的 Partition 上,然後把這個切成 100 甚至 1000 個 Partition 都可以,Partition 是細粒度模塊,服務於每個進程。每個 Partition,比如 Partition 1 可能會有 3 副本,落在不同的機器上,Partition 2 落在不同的 3 台機器上,每個 Partition 內部保持強一致,如果要進行調度,將某個 Partition 移開。

存儲引擎上面是查詢引擎層,查詢引擎是無狀態的,即:所有數據 Graph Service 要麼從 meta 拉元數據,要麼去 storage 拉主數據。查詢引擎本身不存在狀態,引擎相互之間不存在通信,某個查詢過來只會落到某個 graphd 上,而這個 graphd 會落到多個 storaged 上。

以上就是 Nebula Graph 存儲計算分離架構講解。

下面為數據特性,上文說過 Nebula Graph 為屬性圖,雖然點和邊的連接關係弱,但是本身點邊自個擁有什麼屬性是 DDL 預先定義好的,當然你可以設置多版本 Schema。在 Nebula Graph 中稱點類型為 Tag,邊類型為 EdgeType,點可以連接多條邊。用戶使用 Nebula Graph 需要預先指定唯一標識,比如 int64 或者定長 string,點通常使二元組來標識,即:string vid 加上點類型 tag。邊則用四元組來標識,即:起點、邊類型、rank 和終點,所以如果要取 ID 的話,會取出來二元組或者四元組。

在數據類型方面,常見的 Boolean、Int、Double 之類的數據類型,或者是組合類型 List、Set,或者是圖常見 Path、Subgraph 類型都支持。在數據屬性中可能會存在長文本,一般來說交給 Elasticsearch 進行文本索引處理。

這裡對之前的存儲引擎進行補充說明,對外對於查詢引擎 graphd 而言,存儲引擎暴露的對外接口就是分佈式圖服務,但如果需要的話,也能暴露為一個分佈式 KV 服務。在存儲引擎中,Partition 採用多數派一致性協議 Raft。在 Nebula Graph 中點邊是分區存儲,這裡講解下 KV 實現分區存儲原理:

DTCC2021

上圖是一個簡單的圖:起點、邊、終點,現在用 KV 建模,上面是起點 K 的建模,下面是終點 K 的建模,Value 部分是序列化屬性。上面例子將起點和終點做成了 2 個 K,一般來說出現三個圖元素(兩個點加一條邊),數據存儲會落在 2 個不同的 Partition 上:出邊和起點存儲在一塊,入邊和終點存儲在一塊。這樣設計的好處在於,從起點開始的廣度優先遍歷找邊會非常方便,或者終點開始進行逆向廣度遍歷找出起點也會非常方便

所以上面說的就是圖切邊操作:

DTCC2021

在 Nebula Graph 中一條邊存儲為 2 份數據,前面提及存儲層依賴於 VID,基於 Raft 協議保障強一致性,而 Raft 也可以支持 Listener 角色用來將數據同步到其他進程。

再來講解下 Nebula 功能,比如:索引功能,目前 Nebula Graph 用 ES 來做全文索引,在 v2.x 版本開始,研發團隊對 Nebula 索引的寫性能進行了優化。在 v2.5.0 開始也支持了數據過期 TTL 功能和索引功能的組合使用。此外還有 TOSS 功能,在 v2.6.0 版本開始,Nebula 支持了正反向邊的最終一致性,插入、修改邊時,正反邊要麼同時寫入成功,要麼同時寫入失敗。

在查詢引擎和查詢語言 nGQL 方面,圖數據庫查詢這塊目前是沒有統一的標準,目前主要分為 2 個流派:由 Neo4j、TigerGraph、Oracle、Google、LDBC 組織機構正在推進的 Graph Query Language 的國際標準,主要是基於 ISO SQL 標準上的擴展;以及 Tinkerpop 的 Gremlin,這是 Apache 那邊的標準,一般雲產商和程序員對此比較感興趣。目前來說,六月份 IOS SQL 組織的草稿文獻表明 GQL 標準的語法和語義已經大體確定,主要的幾個數據庫廠商達成了一致。個人預計明年可以看到公開的文本。

再來說下 Nebula Graph 的整個圖查詢語言 nGQL 的演變,在 v1.x 版本中,查詢語言是完全自研的,類似 GO STEPS 這樣風格,多個子句用 PIPE 進行連接,比如下面的例子:

GO N TO M STEPS FROM $ids OVER $edge_type WHERE $filters | FETCH PROP

在 v2.x 開始,nGQL 開始兼容 openCypher。openCypher 是 Neo4j 在 17 年開源了部分的 Cypher 語言,目前 nGQL 支持 openCypher 的 DQL(tranversal、pattern match),在 DDL、DML 這些方面,v2.x 版本還是保留了自己原生的 nGQL 風格。

上文提到 Nebula Graph 的四個設計目的有一個點是:生產可用,在運維這塊就得考慮對多圖空間進行數據隔離、用戶權限鑒定、副本自定義、VID 類型不同…此外,在集群級別可以採用多數派一致性協議實現 CAP 中的 CP 方案,也可以跨機房實施 AP 方案。還有運維部署方面,4 月底發佈的 Nebula Operator 支持了 K8s。在運維監控方面,除了 Nebula 研發團隊開發的 Nebula Dashboard 之外,社區的用戶也在不同程度上對 Grafana、Prometheus 做了支持。數據初始化這塊非常豐富,因為 Nebula Graph 底層用了 RocksDB,RocksDB 可以直接在 Spark 集群上將底層數據格式算好,比如說,你在 300 台 Spark 機器中算好數據格式,再導入 Nebula Graph 中,每個小時可以很快地更新一千億點邊數據。

關於性能部分,大多數 Nebula 性能測試都是社區用戶,比如美團、微信、360 金融、微眾銀行之類的技術團隊在做性能測試。Nebula Graph 的性能測試報告,一般是自身版本性能對比,基於公開的 LDBC_SNB_SF100 數據集,大概結果可以參考下面這幾張圖:

DTCC2021

DTCC2021

總的來說,深度遍歷情況下性能有明顯提升

上文提到 Nebula Graph 的第三個設計目標是 OLTP,那麼如何滿足用戶的 AP 需求呢?在這塊 Nebula Graph 對接了 Spark 的 Graph X,以及支持騰訊微信團隊圖計算引擎 Plato。在 Plato 對接這塊,其實是兩套引擎的數據打通,需要將 Nebula 內部的數據格式變成 Plato 中內部的數據格式,Partition 做一一映射,相關的文章將在公眾號後續發佈。

開源社區

最後部分為 Nebula Graph 的社區情況,下圖為 Nebula Graph 研發商歐若數網的一個年表:

DTCC2021

Nebula Graph 項目在 19 年 5 月開源並發佈了 Alpha 版本,20 年 6 月發佈 1.0 GA 版本,雖然之前已經有些企業用戶將 Nebula Graph 應用於生產環境。在今年 3 月發佈了第二個大版本 v2.0 GA,對比之前的 1.x 版本最大的區別是支持了 openCypher。上文也提到過 DB-Engines 排名,Nebula Graph 在 19 年 12 月進入榜單,當時是最後一名目前 2 年之後上升到 15 名:

DTCC2021

然後是國內的某個大學對國內開源產品進行了個社區熱度排名,Nebula Graph 研發商歐若數網(vesoft)排名第八。

DTCC2021

最後,講下對開源的思考:其實在圖領域,開源是一件很常見的事,反而閉源並不常見。因為圖本身在過去幾年是個小領域,只是最近慢慢地火起來。所以,選擇開源是一個挺好的 Branding,建立自己技術品牌的方式。再者,開源的方式可以吸引更多的人來用它,同更多的人交流圖技術,促進彼此思考。以及,在用戶使用過程中,反饋過來的使用建議能迭代、快速完善產品。

QA

下面摘錄 DTCC 現場用戶提問:

Q:圖數據庫和多模數據庫是衝突的嗎?二者的關係是什麼?

A:相比來說多模數據庫(multi-model),圖數據庫最大的特點是在於它的全關聯,如何實現多跳查詢。對應到數據庫設計的話,就是考慮底層要做成一個什麼樣的數據格式,數據和計算怎麼放在一起,這個和 multi-model 本身是不衝突的。為了提升性能,各個多模數據庫處理方法並不一樣:採用不同的存儲引擎,或者是同一套存儲引擎,數據結構可能會做成不同的樣子。

Q:圖查詢設計的出發點是什麼?為什麼不考慮一開始基於 Gremlin 開發?

A:對於數據分析的同學,Gremlin 並不是一個低門檻語言,有些不友好。當時 Gremlin 的設計實現要求每個算子發出來之後必進行執行出來結果,舉個例子,我現在要做一個 .out 和 .in,我必須得先執行 .out 再進行 .in,這樣就不能進行一個全局優化。而 18 年的時候,openCypher 並不完善,小問題有些,表達力不行需要 APOC 進行補充,而 APOC 法律協議並不像 openCypher 那麼確定,所以基於此,我們自研了 nGQL。在 19 年開始,GQL(查詢語言標準化)運動開始了,GQL、Cypher 和 openCypher 關係比較明顯,所以演變成了現在的 nGQL。

本文中如有任何錯誤或疏漏,歡迎去 GitHub://github.com/vesoft-inc/nebula issue 區向我們提 issue 或者前往官方論壇://discuss.nebula-graph.com.cn/建議反饋 分類下提建議 👏;交流圖數據庫技術?加入 Nebula 交流群請先填寫下你的 Nebula 名片,Nebula 小助手會拉你進群~~