圖數據庫的內部結構 (NEO4j)

What “Graph First” Means for Native Graph Technology
在這裏插入圖片描述
Neo4j是一個具有原生處理(native processing)功能和原生圖存儲(native graph storage)的圖數據庫

1.原生圖處理

原生圖處理:存在免索引鄰接屬性,因此她提供快速高效的圖遍歷

  • 解讀:

使用免索引鄰接的數據庫引擎中的每個節點都會維護其對相鄰節點的引用。因此每個節點都表現爲其附近節點的微索引,這比使用全局索引代價小很多。這意味着查詢時間與圖的整體規模無關,它僅和所搜索圖的數量成正比。

相反,一個非原生圖數據庫引擎使用(全局)索引連接各個節點。這些索引對每個遍歷都添加一個間接層,因此會導致更大的計算成本。原生圖處理的擁護者認爲免索引鄰接至關重要,因爲它提供快速、高效的圖遍歷。

索引查找在小型網絡中可以工作,但對於大圖的查詢代價太高。具有原生圖處理能力的圖數據庫在查詢是不是使用索引查找來扮演聯繫的角色,而是使用免索引鄰接來確保高性能遍歷的。
非原生圖處理引擎使用索引進行節點間遍歷
在這裏插入圖片描述
上圖中要尋找Alice的朋友,我們必須要首先執行索引查找,成本爲O(log n ) ,這對於偶爾或者淺層的查找來說是可以接受的,但當我們改變遍歷的方向時,他的代價就變得非常昂貴起來,如果相對於尋找alice的朋友,就必須要執行多個索引來完成查找,每個節點所代表的人都有可能把Alice當作他的朋友,這使得成本很高,找到Alice的朋友代價是O(log n ) ,而找到和Alice交朋友的人的代價則是O(mlogn)。
索引查找在小型網絡中還可以,但是在大圖中的查詢代價太高,具有原生圖處理能力的圖數據庫在查詢時不是使用索引查找的,而是使用免索引零連接來確保高性能的遍歷的,下圖爲Neo4j使用關係而非索引實現快速遍歷
在這裏插入圖片描述

在通用圖數據庫中,可以以極小的代價雙向(從尾部到頭部或者從頭部到尾部)遍歷關係,上圖中尋找ALICE的朋友,直接向外尋找friend就可以。其遍歷的成本爲O(1),要尋和Alice交朋友的人,我們只需要所有指向ALICE的friend關係聯繫在一起即可,這樣的成本是O(1).
###2.原生圖存儲
native graph storage
免索引鄰接(index-free adjacency) 是圖數據庫相比於傳統的 mysql 的優勢的核心 key,那麼圖數據庫用什麼結構去存儲 index-free adjacency 是關鍵設計點。
在這裏插入圖片描述
在這裏插入圖片描述
架構上生層是對外訪問的 api,右邊是事務管理,左邊有 cache 等,下面我們看下 disk 上存儲的結構:在這裏插入圖片描述

neo4j 在磁盤上會分不同的 store file 存儲

neostore.nodestore.db:存儲 node
neostore.propertystore.db:存儲屬性
neostore.relationshipstore.db:存儲關係
一個重要的設計點是 store 中存儲的 record 都是固定大小的,固定大小帶來的好處是:因爲每個 record 的大小固定,因此給定 id
就能快速進行定位。

具體結構是:

在這裏插入圖片描述
節點於關係的存儲文件的物理結構圖
上圖第一個是 node record 的結構:

1byte:in-use flag,表明該 node 是否在使用
4byte:第一個 relation id
4byte:第一個 property id
5byte:label 信息(可能直接 inline 存儲)
1byte:reversed
圖中的節點和聯繫的存儲文件都是固定大小的,每個記錄長度爲9字節,因此可以可以在O(1)的時間複雜度下計算位置.
解釋1:

  • 節點(指向聯繫和屬性的單向鏈表,neostore.nodestore.db):第一個字節,表示是否被使用的標誌位,後面4個字節,代表關聯到這個節點的第一個關係的ID,再接着的4個字符,代表第一個屬性ID,後面緊接着的5個字符是代表當前節點的標籤,指向該節點的標籤存儲,最後一個字符作爲保留位.
  • 聯繫(雙向鏈表,neostore.relationshipstore.db):第一個字節,表示是否被使用的標誌位,後面4個字節,代表起始節點的ID,再接着的4個字符,代表結束個節點的ID,然後是關係類型佔用5個字節,然後依次接着是起始節點的上下聯繫和結束節點的上下節點,以及一個指示當前記錄是否位於聯繫鏈的最前面.

同時還有屬性存儲(neostore.propertystore.db)也是固定大小,每個屬性記錄包括4個屬性塊(一個屬性記錄最多容納4個屬性)和指向屬性鏈中下一個屬性的ID.
屬性記錄包括屬性類型和指向屬性索引文件的指針(neostore.propertysotre.db.index).
同時屬性記錄中可以內聯和動態存儲,在屬性值存儲佔用小時,會直接存儲在屬性記錄中,對於大屬性值,可以分別存儲在動態字符存儲(neostore.propertysotre.db.strings)和動態數組存儲(neostore.propertysotre.db.arrays)中,由於動態記錄同樣由記錄大小固定的記錄鏈表組成,因此大字符串和大數組會佔據多個動態記錄.

解釋2:

節點存儲文件用來存儲節點的記錄。每個用戶級的圖中創建的節點最終會終結於節點存儲,其物理文件是"neostore.nodestore.db"。像大多數Neo4j存儲文件一樣,節點存儲區是固定大小的記錄存儲,每個記錄長度爲9字節。通過大小固定的記錄可以快速查詢存儲文件中的節點。

一個節點記錄的第一個字節是“是否在使用”標誌位。它告訴數據庫該記錄目前是被用於存儲節點,還是可回收用於表示一個新的節點。接下來的4字節表示關聯到該節點的第一個聯繫,隨後4字節表示該節點的第一個屬性的ID。標籤的5字節指向該節點的標籤存儲(如果標籤很少的話也可以內聯到節點中)。最後的字節extra是標誌保留位。這樣一個標誌是用來標識緊密連接節點的,而省下的空間爲將來預留。節點記錄是相當輕量級的:它真的只是幾個指向聯繫和屬性列表的指針。

相應的,聯繫被存儲於聯繫存儲文件中,物理文件是neostore.relationshipstore.db。像節點存儲一樣,聯繫存儲區的記錄的大小也是固定的。每個聯繫記錄包含聯繫的起始點ID和結束節點ID、聯繫類型的指針(存儲在聯繫類型存儲區),起始節點和結束節點的上一個聯繫和下一個聯繫,以及一個指示當前記錄是否位於聯繫鏈最前面。

下面是 relation record 的結構:

剛開始是開始和結束節點的 node id,接着是 relation type pointer,然後開始和結束節點的前驅和後繼 relation id

更形象一點的圖

在這裏插入圖片描述
在這裏插入圖片描述
一個可能的搜索過程是:對於給定的一個 node record,可以通過 id 進行簡單的偏移計算得到 node,然後通過 relation_id 定位到 relation record,然後得到 end node id,通過偏移計算得到 node

兩個節點記錄都包含一個指向該節點的第一個屬性的指針和聯繫鏈中第一個聯繫的指針。要讀取節點的屬性,我們從指向第一個屬性的指針開始遍歷單向鏈表結構。要找到一個節點的聯繫,我們從指向第一個聯繫(在示例中爲LIKES聯繫)的節點聯繫指針開始,順着特定節點的聯繫的雙向鏈表尋找(即起始節點的雙向鏈表或結束節點的雙向鏈表),直到找到感興趣的聯繫。一旦找到了我們想要的聯繫記錄,我們可以使用和尋找節點屬性一樣的單向鏈表結構讀取這種聯繫的屬性(如果有的話),也可以使用聯繫關聯的起始節點ID和結束節點ID檢查它們的節點記錄。用這些ID乘以節點記錄的大小,就可以立即算出每個節點在節點存儲文件中的偏移量。

聯繫存儲文件中的雙向鏈表:

雙向存儲

還有一個問題:圖中節點的關係是有方向的,怎麼記錄這種方向呢?如果方向是雙向的,我們難道要存儲兩個 relation 嗎?

看例子:
在這裏插入圖片描述

這種 partner 的關係天然就是雙向的,但是我們存儲的時候,難道要存儲兩個關係嗎,如下圖:
在這裏插入圖片描述

那肯定是不需要的,這種存儲就是一種浪費,那到底 neo4j 中是怎麼存儲 partner 這種雙向關係的呢?
答案是:以任意一個節點爲開端,另一個爲尾端,即存儲成爲單向的關係

在這裏插入圖片描述

在 neo4j 中任意的關係都有一個 start node 和一個 end node,而且 start node 和 end node 都會有個關聯的雙向鏈表,這個雙向鏈表中就記錄了從該節點出去和進入的所有關係,一個實例是:

在這裏插入圖片描述

圖片來源:neo4j 底層存儲結構分析
上圖中 B 節點的 prev 和 next 我們就能看到在這個鏈表中,B 有時候是 start node 有時候是 end node。
至此我們就對圖數據庫有了個大概的瞭解了,後續的分析會隨着項目的推進持續輸出。

參考文獻:

https://neo4j.com/blog/native-vs-non-native-graph-technology/
https://graphaware.com/neo4j/2013/10/11/neo4j-bidirectional-relationships.html
https://www.tuicool.com/articles/NFzmQn

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