使用祕籍|如何實現圖數據庫 NebulaGraph 的高效建模、快速導入、性能優化

本文整理自 NebulaGraph PD 方揚在「NebulaGraph x KubeBlocks」meetup 上的演講,主要包括以下內容:

  • NebulaGraph 3.x 發展歷程
  • NebulaGraph 最佳實踐
    • 建模篇
    • 導入篇
    • 查詢篇

NebulaGraph 3.x 的發展歷程

NebulaGraph 自 2019 年 5 月開源發佈第一個 alpha 版本以來,陸陸續續發佈了 2.0 GA,到現在 v3.6.0,已經是 v3.x 版本中比較後期的版本了。從 3.x 開始,基本上保持了三個月(一個季度)發一個 y 版本的節奏,從 v3.1 到 v3.2,到現在的 v3.6。(演講時 v3.6 尚未發佈,所以沒有相關內容展示)

而這幾個版本主要是在完備性、性能,穩定性以及易用性上的優化。從 NebulaGraph 作爲一款圖數據庫的產品定位上來說,核心應用場景是 TP 場景,而對 TP 型數據庫產品來說,有幾個特性是非常重要的:

  • 穩定:數據庫作爲底層的基礎設施,許多業務是基於數據庫運行的,是在線的系統,因此穩定性是一個非常重要的特性。在 NebulaGraph v3.4 版本,包括最新版本的 v3.6 版本中,系統的穩定性得到了非常大的提升和改善;
  • 性能:因爲 NebulaGraph 定位是一個 TP 的數據庫產品,因此性能,包括高吞吐量是至關重要的;
  • 易用:一個好的產品要如何讓用戶更好地用起來,降低用戶的學習成本,也是需要考慮到的;

針對這些特性,我們在 v3.x 這 5 個版本中做了這些嘗試:

穩定性

在 v3.x 開始,NebulaGraph 引入 fuzzer,極大提升測試效率。fuzzer 可基於 nGQL(NebulaGraph 的查詢語言)的語法進行靈活組合,生成人爲不能擬定的查詢語句,由此讓測試更加完善,從而提高了穩定性。

此外,版本新特性部分還新增 Memory Tracker。圖數據庫不同於其他數據庫,數據一直處於持續地遍歷、迭代中,因爲即便是數據量不大的情況下,數據的迭代會導致它的結果異常大,這就造成了內存的管理壓力。在 v3.4 版本中,NebulaGraph 引入了 Memory Tracker 機制,從論壇的用戶反饋上,可以看得出來相關的 OOM 問題大幅度減少了。這裏可以閱讀下《內存管理實踐之 Memory Tracker》

性能提升

圖的經典查詢一般包括 K 跳(K-hop)、子圖和路徑查詢。K 跳就是從一個點出發,比如說從我出發,去找尋我好友(一跳)的好友(兩跳),這種查詢,可能社交或者反欺詐的場景中使用會比較多。此外,就是子圖,比如說我現在從一個點出發,找到他的周圍的關聯的一羣人,以及這一羣人關聯的另外一羣人,這時候就可能會用到子圖的功能。還有就是路徑查詢,像是企業和企業之間的關聯關係之類的,就比較適合用路徑,來找尋二者的關聯。在 v3.x 中,這幾個圖的經典查詢性能都有大幅度的提升的,具體大家可以看論壇的性能報告:自測和他測報告合集

上面提到過內存管理的難點,除了 Memory Tracker 機制之外,屬性裁剪能提升內存的利用率,在 NebulaGraph 中如果查詢不需要用到某個屬性,就會將其裁剪掉,從而提升內存利用率。

易用性

NebulaGraph 在 v2.x 開始支持 openCypher,一開始是比較基礎的 openCypher 語法;在 v3.x 開始,NebulaGrpah 做了一個語法完善,像是 OPTIONAL MATCH、多 MATCH 等語法支持,全面覆蓋了國際圖基準測試之一的 LDBC-SNB 支持的圖查詢。

此外,在 v3.5 開始支持了 UDF 功能,這個功能是由社區用戶 zhaojunnan 提供支持的,它可以用來幫助實現一些內核暫時不支持的功能。這裏就不詳細展開 UDF 的說明了,具體大家可以看《NebulaGraph UDF 功能的設計與背後的思考》

最後一點是全文索引優化,這個在後面章節會詳細講述。

NebulaGraph 的最佳實踐

在這個部分主要分爲:建模、數據導入、查詢等三大內容。

數據建模

數據膨脹

這是社區用戶在交流羣裏反饋的一個問題:

  1. 數據導入之後佔用硬盤空間極大,60 MB 的文件導入之後,Storage 佔用了 3.5 G;
  2. Storage 服務佔用內存很高
  3. 併發量大的時候,會出現 ConnectionPool 不夠的情況;
  4. 3 跳查詢本來就很慢麼?我看官方的 Benchmark 數據規模大的時候會慢,但是當前我們數據量也不是很大;

目前的數據情況:點 594,952,邊 798,826。同時,邊和點均建了 1 個索引:

所以它有什麼問題呢?

在圖數據庫 NebulaGraph 中,有個概念叫做 Space,Space裏有一個概念是 VID type,VID 是 NebulaGraph 中非常關鍵、重要的概念,所有的數據字段都是通過 VID 來進行唯一索引,類似主鍵的概念。比如上圖的中間部分:點結構和邊結構,可以看到結構中都有 VID,用來進行字段查詢;邊結構還分起點 srcId 和終點 dstId,就是上圖的兩處 VertexID。

如上圖所示,這個 Space 中配置的 VID 類型是 String,而 NebulaGraph 支持的 VID 類型有兩種:一種是 INT,數值類型,像手機號之類的可以用 INT 來存儲;一種是 String,比如說人名之類的,當然你要用 String 來存儲像是身份證號之類的數值信息也可以。但是,用 VID 的查詢效率從經驗看是 INT 類型是遠高於用 String 作爲 VID 類型的。

回到上面的這個例子,一開始用戶創建 VID 時,直接選取了 FIXED_STRING 類型,設定爲了 256 位的定長 String。但是這裏會導致一個問題:

  • 594,952(點數)*256 (VID 大小)* (1 + 1) + 798,826(邊數)* 256(VID 大小) * (2 + 2 + 2 + 2) = 1.80 GB

上面的例子是數據存儲的大小計算過程,點的數量乘以定長的長度(這裏是 256),再乘以佔據的字節大小,以及邊的數量乘以對應 VID 的長度,再乘以對應邊 VID 佔據的空間大小,算出來是 1.8 GB。由此,我們可以想到一個事情:是不是可以精簡下 VID 的定長長度,設置一個合理的數值,比如說是 32,那它空間佔據量就是:

  • 594,952(點數)*32(VID 大小)* (1 + 1) + 798,826(邊數)* 32(VID 大小)* (2 + 2 + 2 + 2) = 0.23 GB

修改 VID 的定長長度之後,整個空間使用量就是之前的 1/8,還是非常可觀的一個磁盤容量優化。如果是更多的點和邊數據量的話,縮減的磁盤空間會更客觀。由此,我們有個建議:VID 的定長長度儘可能短,同理,屬性類型設置亦如是

超級節點

圖數據庫實踐中,超級節點是一個比較常遇到的性能問題。那麼,什麼是超級節點(稠密點)呢?圖論給出的解釋是:一個點有着超級多的相鄰邊,相鄰邊就是出邊(從這個點指向另外一個點)或者是入邊(某個點指向這個點)。像是社交網絡中 KOL、網紅大V 之類的人,或是證券市場的熱門股票,交通網絡中的樞紐站、銀行系統中的四大行、互聯網中的高流量站點,或者是電商平臺的爆款商品,等等都是整個關係網絡中的超級節點。一旦查詢中有超級節點,查詢速率就會變得異常的緩慢,甚至有時候內存都耗盡了,查詢結果還沒跑出來。

下面就來講講,現階段你要用 NebulaGraph 可以如何解決或是繞開超級節點:

要在建模環節規避掉超級節點的問題,“拆點”是可行的方式之一。如上圖左側所示,在未優化建模之前,A 通過 transfer 邊關係連接到 B1、B2,如果 A 頻繁的轉賬,勢必會導致它成爲一個超級節點。這時候,你可以將 A 拆分成 A1 和 A2,按照某種約定的方式,比如說轉賬的日期,或者是由單一客戶拆分成對公客戶、對私客戶,從而達到拆點、避開超級節點形成的目的。不過,這裏會涉及到一個 VID 變更的問題,將 A 拆分成 A1 和 A2,會導致對應的 VID 發生變化,當然你可以命名 A1 爲 A0721,A2 爲 A0722,加上日期數字來標識它們。

相對應拆點,還有拆/合邊的方式。在兩個點之間,有許多同一類型的邊,比如說轉賬關係,這時候,可以根據業務的邏輯來進行判斷,比如取最短邊、最新邊、最大邊、最小邊等,在一些不需要明細的場景裏,只體現關係出來,這樣就能提升查詢效率。除了合併之外,拆邊也是一種方式,如上圖右側所示,兩個點之前有非常多的關係,它們都是交易類型,可能有一部分是發紅包,有一部分是轉賬,這時候,你就可以按照拆點的邏輯,將邊進行拆解。

此外,還有截斷,NebulaGraph 有個配置參數是 max_edge_returned_per_vertex,用來應對多鄰邊的超級節點問題。比如我現在 max_edge_returned_per_vertex 設置成 1,000,那系統從點 A 出發,遍歷 1,000 個點之後就不再遍歷了,便將結果返回給系統。這裏會存在一個問題,加入 A 和 B1 之間存在 1 千多條邊,A 和 B2 存在 3 條邊,按照這種遍歷 1,000 條邊之後就不再遍歷的設定,可能返回結果中 A 和 B2 的關係邊就不會返回了,因爲這個遍歷返回是隨機的。

其他的話,同相關的社區用戶交流,我發現在許多業務場景中,超級節點並沒有太大的實際業務價值。這裏就要提下“超級節點的檢測”,比如:通過度中心性算法(DegreeCentrality)計算出出入度大小,這個圖算法 nebula-algorithm 和 nebula-analytics 都支持。當這個算法跑完之後,得到的二維表就能告訴你哪些是超級節點,提早讓用戶知道哪些點會影響查詢效率。

此外,假如現在你有一個已知的超級節點,且不方便處理,那查詢的時候就要儘量避免逆向查詢,即從這個超級節點出發,查詢其他節點。

數據導入

社區用戶經常遇到的還有一類問題:數據導入慢的問題。一般新的社區用戶都會問:你們的導入性能如何?這時候我們一般會說:導入性能老牛逼了,而且我們是直接用 INSERT 方式導入的,速度賊快,之前遇到最快的是 600MB/s。

這時候用戶一般會反問:爲什麼我測試出來,導入速度沒有官方說的那麼快。

這裏就展開說說如何提升你的數據導入性能。

熟悉 NebulaGraph 的小夥伴都知道,它的底層存儲是基於 RocksDB 實現的,而 RocksDB 有 wal_ttl 這麼一個配置項,如果你的導入數據量非常大,對應的 wal 日誌也會相對應的變大。因此,建議在進行數據導入時,將 wal_ttl 時間設短一點,以防止膨脹的 wal 日誌過度地佔用磁盤,可以得到及時的清理。此外,就是 Compaction 相關的配置項,主要是 max_subcompactionsmax_background_jobs 這兩個參數項,一般建議將其設置爲 CPU 核數的一半。而這個一半的參數建議,主要來源於用戶的反饋以及一些經驗數據,不同的場景還是需要不同的配置,HDD 和 SSD 的配置也有所不同,大家可以後面看着情況進行調試。

除了配置參數之外,在做數據導入之前,建議大家執行下 SHOW HOSTS 操作,查看 leader 是否分佈均勻:NebulaGraph 會將數據分爲若干個 partition,每個 partition 會隨機分佈在節點上,理想狀態自然是 partition 的 leader 是均勻地分佈在各個節點的。假如 leader 分佈不均的話,可以執行 BALANCE LEADER 操作,確保其均勻分佈。

在工具配置方面,可能就是數據導入的重頭戲了,配置你的數據導入工具參數:

  • 配置項 concurrency,表示導入工具連接多少個 graphd(查詢)節點,一般設置爲導入工具 nebula-importer 所在機器的 CPU 核數;
  • manager.batch,雖然 NebulaGraph 支持你通過 INSERT 來一個個點插入到數據庫中,但是這個有些低效。因此,設立了 batch 字段用來將一批數據導入到數據庫中,默認參數設置是 128,不過這裏要根據你自身的數據特性來進行優化。假如你的屬性值很多,那麼建議將 batch 調小;反之,將 batch 值調大即可。整個 batch 的大小,建議小於 4MB;
  • manager.readerConcurrency 是數據讀取的併發數,即,從數據源讀取數據的併發數。默認參數是 50,一般建議設置爲 30-50 即可;
  • manager.importerConcurrency,數據讀取之後,會根據一定的規則拼接成 batch 塊,這裏就涉及到這個參數項。manager.importerConcurrency 指的是生成待執行的 nGQL 語句的協程數,一般來說它會設置成 manager.readerConcurrency 字段的 10 倍,默認值是 512;

軟件說完了,來說下硬件方面的配置。NebulaGrpah 優先推薦使用 SSD,當然 HDD 也是可以的,不過性能相對會差點。此外,在 data_path 下多配置幾塊盤,每個路徑配置一個盤,這個也是之前的實踐經驗總結出來的。而機器和機器之間,推薦使用萬兆網卡。最後一點是,nebula-importer 之類的導入工具有條件的話儘量單獨部署,和集羣隔離開,不然的話在一臺機器人會存在資源搶佔的問題。

軟硬件都說完了,剩下就是數據本身的問題。圖數據庫的定位是關係分析,同此無關的事情,例如:全文搜索(ES 擅長的場景),要看情況是否將該部分數據放入到 NebulaGraph 中。由於 NebulaGraph 進行數據導入時,不存在導入的先後順序,即點和邊一起混合導入,這樣設計的好處是,數據無需做預處理,壞處是數據導入之後可能會產生懸掛邊,不利於後續的查詢。最後要留意起點,或終點爲空的數據,或者是異常數據,這些數據在異常處理時很容易一不小心形成超級節點。

查詢指南

下面來講講如何搞定 NebulaGraph 的查詢篇。這裏是一些 tips:

  • MATCH 性能比 GO 略慢,但 MATCH 是我們優化的重點。如果沒有強性能需求的話,推薦還是儘量使用 MATCH,表達能加豐富之外,它同將要出爐的 ISO GQL(圖查詢語言)是匹配的;
  • 慎用函數(無法下推),在 NebulaGraph 中並沒有將函數下推到 storage。因此,像 src(edge)dst(edge)rank(edge)properties($$) 之類的函數,性能都不如 edge_.srcedge._dstedge._rank$$.tag.prop 這些下推到 storage 的表達;
  • 遇到聚合且需要取屬性的情況,先聚合再取屬性,因爲取屬性耗時較長;
  • MATCH 如果只是最後需要返回 count,那麼對於 count 的變量最好採用 count(id(v)) 類似的形式,這樣會應用到屬性裁剪,減少內存消耗;
  • 能不帶路徑儘量不要帶路徑,帶路徑需要進行路徑構造,屬性裁剪會失效,此外,還會增加很多額外的內存開銷。

總的來說,減少模糊、增加確定,越早越好

內存保護試試 Memory Tracker

在 v3.4 版本中,引入的一個大功能是:Memory Tracker,用來保護內存,防止內存佔用過大導致的 OOM 問題。

  • 預留內存:memory_tracker_untracked_reserved_memory_mb(默認 50 MB)。Memory Tracker 機制會管理通過 new/delete 申請內存,但進程除了通過此種方式申請內存外,還可能存在其他方式佔用的內存;比如通過調用底層的 malloc/free 申請,這些內存通過此 flag 控制,在計算時會扣除此部分未被 track 的內存,所以這裏預留了 50 MB;
  • 內存比例:memory_tracker_limit_ratio,就是實際可用內存的比例佔用多少的情況下,會限制它再申請使用內存。一般默認是 0.8,就是這個內存佔用小於 0.8 的情況下,是可以隨意使用內存的;當系統內存佔用超過 80% 時,系統便會拒絕掉新的查詢語句;
    • 數值範圍:(0,1],默認 0.8 且爲開啓狀態。大多數的用戶的 storage 和 graph 節點都存在混部情況,這時候就會建議調低 memory_tracker_limit_ratio,順便說一句,這個參數項是支持在線調整的;
    • 數值配置成 2,則會對其進行動態調整,這個動態分配的內存佔用比例可能會不大精準;
    • 數值配置成 3,則關閉 Memory Tracker 功能;

此外,你如果要調試 Memory Tracker 的話,可以開啓 memory_tracker_detail_log 來獲得調試日誌,這個參數項默認是關閉的。

經測試,Memory Tracker 對性能有 1% 左右的影響,但是對於上層爲平臺類產品或者交互式分析類產品,強烈建議打開。爲什麼呢?因爲上層的業務同學不大瞭解 NebulaGrpah 運行機制的情況下,容易將服務打滿,導致內存爆炸,因此開啓這個功能之後,至少能保證系統的穩定運行。

最後,如果動態申請內存時,返回報錯 GRAPH_MEMORY_EXCEEDED/STORAGE_MEMORY_EXCEEDED 說明這個內存已經不夠用,這條查詢語句將不會執行(被殺掉)。

語句調試得用 PROFILE

在任意一條 nGQL 查詢語句前面加入 PROFILE,並能得到這條語句的執行計劃。

上圖一條語句的整個生命週期,Planner 是執行計劃(Execution Plan)生成器,它會根據 Validator 校驗過、語義合法的查詢語法樹生成可供執行器(Executor)執行的未經優化的執行計劃,而該執行計劃會在之後交由 Optimizer 生成一個優化的執行計劃,並最終交給 Executor 執行。執行計劃由一系列節點(PlanNode)組成。而下圖則是一些常見的算子,上圖每一個 Plan 節點對應了一個算子:

算子 介紹
GetNeighbor 根據指定的 vid ,從存儲層獲取起始點和邊的屬性
Traverse 僅用於 MATCH 匹配 ()-[e:0..n]-() 模式,獲取拓展過程中的起始點和邊的屬性
AppendVertices MATCH 使用,同算子 Traverse 配合獲取點的屬性
GetEdge 獲取邊的屬性
GetVertices 獲取點的屬性,FETCH PROP 或者 GO 語句中。
ScanEdge 全表掃描邊,例如 MATCH ()-[e]->() RETURN e LIMIT 3
ScanVertices 全表掃描點,例如 MATCH (v) return v LIMIT 3
IndexScan MATCH 語句中找到起始點的索引查詢
TagIndexPrefixScan LOOKUP 語句中前綴掃描 LOOKUP ON player where player.name == "Steve Nash" YIELD player.name
TagIndexRangeScan LOOKUP 語句中範圍掃描 LOOKUP ON player where player.name > "S" YIELD player.name
TagIndexFullScan LOOKUP 語句中全掃描 LOOKUP ON player YIELD player.name
Filter 按條件過濾,例如 WHERE 語句
Project 獲取上一步算子的列
Dedup 去重
LeftJoin 合併結果
LIMIT 限制輸出行數

下面這個是一個例子,我們可以結合例子講解下。一般來說 PROFILE 會生成一個執行計劃,同 EXPLAIN 生成執行計劃不同,PROFILE 生成的執行計劃中會有相對應的執行時間在裏面,比如說下面這張圖:

一般來說,我們看執行計劃不只是看上下的調用關係,還需要去看裏面的具體執行細節:

  • execTime:graphd 的處理時間;
  • totalTime:graphd 算子起到到算子退出時間;
  • total_rpc_time:graphd 調用 storage client 發出請求到接收到請求時間;
  • exec:storaged 的處理時間,同上面的 graphd 處理時間的 execTime
  • total:storage client 接收到 graphd 請求到 storage client 發送請求的時間,即 storaged 本身的處理時間加上序列化和反序列化的時間;

除了查看時間之外,我們還要查看 row 就能看到 graphd 和 storaged 的具體通信量大小。上圖有 3 個 Partition,每個 Partition 返回 1 個 limit 1,總共就 3 條數據。

此外,還得查看執行計劃中是否包含計算下推:

上面兩條查詢語句的差異,上面提到過,就是將函數改成其他調用方式,將 properties(edge).degree 改爲 follow.degree,很明顯地看到計算下推了。

某個功能不支持array

因爲產品規劃的問題,NebulaGraph 可能有些功能沒法直接支持。比如用戶反饋的:

當前屬性僅支持基本類型 long、string,來構建索引。是否可以支持多值,比如:long[]string[] 來構建索引?

的確目前不支持 array,有什麼曲線救國的法子?這裏提供一些方法,僅供參考:

  1. 把數組放到 string 裏,進行查詢時,將數據讀取出來進行解析,雖然有點不優雅,但是能解決問題;
  2. 轉化成 bitmap,將不同的類型組成 bitmap,雖然導致代碼會複雜點,但可以獲得比較快的過濾;
  3. 邊上的 array 轉換成兩點之間的平行邊,相當於一條邊就是一個屬性,可以方便地進行屬性過濾,當然它會帶了額外的邊數量增加問題;
  4. 點上的 array 轉化成自環邊,弊端第 3 種方式,會產生大量自己指向自己的平行邊;
  5. 把屬性作爲 tag,比如我現在有個商品,它在北京、上海、杭州都有倉庫,這時候可以將這個貨點變成一個 tag 屬性,從而方便地對其進行查詢。這裏需要注意的是,這個方式容易產生超級節點,這裏就需要注意避免超級節點的產生。

功能更強了 UDF

用戶自定義函數(User-defined Function,UDF),用戶可以在 nGQL 中調用函數。與從 nGQL 中調用的內置函數一樣,UDF 的邏輯通常擴展或增強了 nGQL 的功能,使其具有 nGQL 沒有或不擅長處理的功能。UDF 被定義後可以重複使用。

這裏簡述下 UDF 的使用過程:

  1. 準備編譯環境 & 下載源碼:https://docs.nebula-graph.com.cn/3.5.0/4.deployment-and-installation/1.resource-preparations/

  2. 進入到 NebulaGraph 代碼倉庫,創建 UDF 相關源碼文件。當前有兩個示意文件 standard_deviation.cpp / standard_deviation.h 可以參考

  3. 編譯 UDF

g++ -c -I ../src/ -I ../build/third-party/install/include/ -fPIC standard_deviation.cpp -o standard_deviation.o
g++ -shared -o standard_deviation.so standard_deviation.o
  1. 加載 UDF 至 graphd 服務

編輯 graphd 服務配置文件:打開 /usr/local/nebula/etc/nebula-graphd.conf 文件,添加或修改以下配置項:

#  UDF  C++
--enable_udf=true
#  UDF .so
--udf_path=/home/foobar/dev/nebula/udf/
  1. 重啓 graphd
udo /usr/local/nebula/scripts/nebula.service restart graphd
  1. 連接到 graphd 後驗證
GO 1 TO 2 STEPS FROM “player100” OVER follow YIELD properties(edge).degree AS d | yield collect($-.d) AS d | yield standard_deviation($-.d)

不過,目前 UDF 有些問題:

  1. so 包位置只支持掃描本地,也就是如果你是分佈式集羣的話,每個機器上都得有個包;
  2. 函數只在 graphd 層,無法下推到存儲;
  3. 暫不支持 Java(性能考慮),未來版本會支持;

待解決的問題

這裏羅列下未來的產品可優化點:

  1. 全文索引:v3.4 之前的全文索引功能都不太好用,約束比較多,且有些 bug。v3.4 版本做了精簡和優化,更加穩定。但實際上,v3.4 及之前的全文索引功能準確講並不是真正意義的全文索引,主要是支持前綴搜索、通配符搜索、正則表達式搜索和模糊搜索等。並不支持分詞、以及查詢的分數,v3.6 版本(即將發佈)做了全文索引的優化,重新設計了全文索引功能(可以更好的支持 Neo4j 替換)。不過與原有的全文索引不兼容,需要重建索引;
  2. 關於懸掛邊的產生:設計理念,不隱式的對數據進行變更導致(刪除點的時候不隱式刪除邊),當然由此會帶來懸掛邊和孤兒點的問題,後面這塊會考慮進行相關的優化;
  3. 事務的支持,大多數人對 NebulaGraph 的事務需求來源於,他認爲 NebulaGraph 是一款 TP 產品,TP 產品是一定具備事務性的,這並非是業務場景的需求。當然,事務這塊撇開這種某款產品必須具備的特性之外這點,一些生產鏈路上面,事務還是一個強需求,因此在後續的開發中也會新增事務特性。

其他問題交流

下面問題整理自本次分享的 QA 部分:

Q:上文提到 VID 的設定,是越短越好。短的 VID 會帶來什麼後果麼?

方揚:VID 理論上是越短越好,沒有任何的副作用。不過它的設定是在滿足你既有的業務需求,不要出現重複的 VID 情況下,儘可能的短即可;

Q:截斷的話,是對返回的數據量做限制,這個返回的話是有序的麼?

訓燾:目前數據的返回是 random,隨即返回的,隨機根據你的 range 來返回一些數據量。


謝謝你讀完本文 (///▽///)

如果你想嚐鮮圖數據庫 NebulaGraph,記得去 GitHub 下載、使用、(з)-☆ star 它 -> GitHub;和其他的 NebulaGraph 用戶一起交流圖數據庫技術和應用技能,留下「你的名片」一起玩耍呀~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章