1. 程式人生 > >Nebula Storage 2.0 儲存格式

Nebula Storage 2.0 儲存格式

![storage-format-in-nebula-graph-2.0](https://www-cdn.nebula-graph.com.cn/nebula-blog/storage-format-in-nebula-graph-2.0.png) 隨著 2.0 各版本的陸續釋出,Nebula Graph 迎來了一系列的改動,在儲存方面,影響最大的改動就是**底層編碼格式進行了修改**。Nebula Graph 的底層儲存是基於 KV 儲存在 RocksDB 中,本文將介紹新老編碼格式的差異,以及為什麼要修改儲存格式等一系列問題。 ## 1.0 版本的格式 我們先簡單回顧下 1.0 版本的編碼格式,不熟悉的可以參考這篇部落格[《Nebula 架構剖析系列(一)圖資料庫的儲存設計》](https://nebula-graph.com.cn/posts/nebula-graph-storage-engine-overview/)。由於在 1.0 版本中,點的 ID 只能夠用整型來表示,所以底層所有 VertexID 都是以 int64 來儲存的。 * 點的格式 ![Nebula Graph 1.0 點的格式](https://www-cdn.nebula-graph.com.cn/nebula-blog/nebula-storage-v1-vertex-format.png) * 邊的格式 ![Nebula Graph 1.0 邊的格式](https://www-cdn.nebula-graph.com.cn/nebula-blog/nebula-storage-v1-edge-format.png) 給定任何一個 VertexID,經過 hash,可以得到對應的 PartID,因此對於一個點和這個點的所有邊(邊用起點計算 hash),都會對映到同一個分片中。**需要指出的是,在 1.0 版本中,點和邊的第一個位元組的 Type 是相同的**。也就是說,對於一個點而言,它的所有 tag 並沒有在物理上連續儲存,比如可能是如下儲存的。對於這個 src 這個點的三個 tag(tag1 tag2 tag3),實際上可能會被其他邊隔開。 ![rocksdb key 儲存](https://www-cdn.nebula-graph.com.cn/nebula-blog/rocksdb-key.png) 這個格式能夠滿足 1.0 絕大多數介面的需要,比如 `fetch` 和 `go` 都只需要指定對應字首,就能獲取對應資料。 ## 2.0 版本的格式 在 GA 之前釋出的版本,底層儲存格式其實和 1.0 是基本相同的。如果 VertexID 是整型,和 1.0 格式完全一致。而如果 VertexID 型別支援 string,則從佔用 8 個位元組的 int64 改成了固定長度的 `FIXED_STRING`,長度需要使用者在 `create space` 時候指定長度。對於不足的長度系統自動使用 `\0` 補齊,而超過指定長度的 VertexID 會直接報錯。 在 GA 版本中,我們對底層儲存格式進行了若干改動,因此這次版本升級時需要通過升級工具,將原有格式的資料轉換為新格式的資料。如下是在 2.0 GA 版本中採用的儲存格式。 ### 2.0 版本儲存格式 * 點的格式 ![Nebula Graph 2.0 點的格式](https://www-cdn.nebula-graph.com.cn/nebula-blog/nebula-storage-v2-vertex-format.png) * 邊的格式 ![Nebula Graph 2.0 邊的格式](https://www-cdn.nebula-graph.com.cn/nebula-blog/nebula-storage-v2-edge-format.png) ### 和 1.0 儲存格式對比 ![點格式版本對比](https://www-cdn.nebula-graph.com.cn/nebula-blog/nebula-storage-vertex-format-comparison.png) ![點格式版本對比](https://www-cdn.nebula-graph.com.cn/nebula-blog/nebula-storage-edge-format-comparison.png) 其中有幾個比較大的改動: 1. VertexID 的長度如前文所說,從固定的 8 位元組,修改為 n 個位元組。VertexID 型別為整型時,n 為 8,VertexID 型別為 string 型別時,n 為指定長度。 2. 點去掉了 1.0 的時間戳。邊將 1.0 時間戳改為了一個位元組的佔位符。 3. 對於點和邊的第一個位元組,**不再使用同一個 Type**,在物理上點和邊進行了分離。 這些改動主要是基於以下幾點進行考慮的: 1. VertexID 改變主要是為了支援 string ID 同時相容 1.0 版本 int ID。在 storage 中,把 VertexID 都處理為 bytes,只在返回結果時根據 space 的設定不同,返回相應型別的 VertexID。 > 為什麼 string ID 要使用 FIXED_STRING ? 如果不使用固定長度,則無法使用字首進行掃描。通過長度不足補齊,使得所有點之間和邊之間的各個字首長度相同,從而進行相應的字首查詢。 2. 去掉時間戳主要是因為儲存多版本資料會影響效能,另外一段時間內暫時不考慮做 MVCC 的相關工作。 > 在邊裡面還保留一個位元組的佔位符,主要是留給 TOSS(transaction on storage side)使用。主要用於標識一條邊的出邊和入邊是否完整插入了,這裡不詳細介紹,後續會有其他文章進行詳盡的分析。 3. 點和邊分離的好處主要是能夠方便快速拿某個點的所有 tag(在Cypher 的 `MATCH` 語句中大量使用)。如果按原先同一 `Type + VertexID` 字首掃描,由於點邊可能摻雜在一起,會極大影響效能。而 Type 分離之後,按 `VertexType + VertexID` 字首掃描,可以快速獲取所有 tag。 > 在 1.0 版本中,由於沒有取某個點的所有 tag 的需求,因此點和邊可以按同一個字首儲存。不過在程式碼層面,還是有不小影響,例如 fetch 介面在 1.0 是按 VertexID 的字首去掃描的,對於超級大點來說取 tag 效能比較差。另外如果使用 storage 提供的 scan 介面,想要獲取全圖的所有點,實際是掃描了整個 RocksDB。 除了點和邊的格式相關改動之外,索引的格式其實也有所改變。 一方面是 2.0 支援 `NULL` 後,索引也需要能夠表示對應的語義。另一方面是在 1.0 的版本中,對於索引中 string 的欄位的處理,實際是按變長 string 處理。因此在 `LOOKUP` 語句中只要使用了帶 string 欄位的索引,就只能使用等值查詢。而在 2.0 的版本中,索引的 string 欄位和資料中的 VertexID 一樣,使用固定長度的 FIXED_STRING,`LOOKUP` 語句中帶 string 欄位的索引能夠使用範圍查詢,例如 `LOOKUP ON index1 WHERE col > "aaa"`。有關索引部分的功能和修改,後續也會再有其他文章介紹。 以上,為本次 Nebula Storage 2.0 儲存格式講解。 喜歡這篇文章?來來來,給我們的 [GitHub](https://github.com/vesoft-inc/nebula-graph) 點個 star 表鼓勵啦~~