主流圖數據庫Neo4J、ArangoDB、OrientDB綜合對比:架構分析

1: 本地存儲方式
2: 內置查詢語言分析
3: 性能分析
4: 圖算法支持javascript


本地存儲方式

Neo4J

neo4j數據庫支持最大多少個節點?最大支持多少條邊?html

  • 目前累積統計它有34.4億個節點,344億的關係,和6870億條屬性。

在數據庫中,讀/寫性能跟節點/邊的數量有關嗎?java

  • 這個問題意味着兩個不一樣的問題。單次讀/寫操做不依賴數據庫的大小。無論數據庫是有10個節點仍是有1千萬個都同樣。 — 然而,有一個事實是若是數據庫太大,你的內存可能沒法徹底緩存住它,所以,你須要頻繁的讀寫磁盤。雖然不少用戶沒有這樣大尺寸的數據庫,但有的人卻有。若是不巧你的數據庫達到了這個尺寸,你能夠擴展到多臺機器上以減輕緩存壓力。

是否有備份恢復機制?node

  • Neo4j 企業版提供了一個在線備份(完整備份和增量備份)功能。

寫數據庫是線程安全的嗎?python

  • 無論在單服務模式仍是HA模式,數據庫在更新以前都經過鎖定節點和關係來保證線程安全。

文件存儲結構

node,relationship,property存儲都是固定大小的。 以下圖:算法

  • 固定大小能夠快速查找,基於此,能夠直接計算一個節點的位置,時間複雜度$O(1)$,比查詢的$O(log n)$快。
image.png

節點

存儲文件neostore.nodestore.dbsql

  • 第一個字節,是否被使用的Flag
  • 下4個字節,表明第一個關係的ID,鏈接到這個節點上的 :ID
  • 緊接着的4個字符,表明第一個屬性ID,鏈接到這個節點上的
  • 緊接着的5個字符是表明當前結點的Label,指向Label存儲的。 :LABEL
  • 最後一個字符是標誌字符,用來標誌緊密相鄰的點,或者留備後用。 這就是Neo4J索引實現的方案。Index-Free Adjacency

這些指向的ID都是鏈式ID中的第一個,好比關係ID是關係鏈中的第一個。docker


關係

存儲文件neostore.relationshipstore.db數據庫

  • 1:同上
  • 1-5:第一個節點
  • 5-9:第二個節點
  • 9-13:關係類型 :TYPE
  • 13-21:前一個關係的先後節點 ID
  • 21-29:後一個關係的先後節點 ID
  • 29-33:屬性ID
  • 34:是都是關係鏈中的第一個的標誌

關係是雙向鏈表,屬性是單向鏈表ubuntu


屬性文件

存儲文件neostore.propertystore.db

  • 每一個屬性文件包含4個屬性塊,一個指向下個屬性的ID,在屬性鏈中
  • 屬性包括屬性類型,指向屬性索引文件neostore.propertystore.db.index的指針
    • 屬性是鍵值對存儲的,數據類型可使用JVM的全部私有屬性,加上字符串和數組類型;
    • 屬性值也可使用動態存儲,就是大文件,類型以下:字符串&數組

查找時文件調用模型

image.png

  • 節點包含指向關係鏈和屬性鏈的第一個指針。
  • 指向Label的指針,可能多個。
  • 屬性讀取從單向鏈表的第一個開始
  • 關係讀取直接在雙向鏈表中查找,直到找到想要的關係。

Index-Free Adjacency

查詢時,算法複雜度,$O(n)$,$n$是節點數,其餘常規索引的複雜度都是$O(n log n)$

刪除修改一個有不少不少邊的節點時會有點麻煩,由於沒有常規索引,只能從關係鏈中開始刪除。

  • 爲了去除全部的事件邊,你必須訪問每一個相鄰的頂點,而且爲每一個相鄰的頂點執行一個潛在的昂貴的移除操做。

ArangoDB

文檔在ArangoDB中的存儲格式很是相似JSON,叫作VelocyPack格式的二進制格式存儲。

  • 文檔被組織在集合中。
  • 有兩種集合:文檔(V),邊集合(E)
  • 邊集合也是以文檔形式存儲,但包含兩個特殊的屬性_from_to,這兩個屬性被用來建立在文檔和文檔之間建立關係

存儲空間佔用下:採用了元數據模式存儲數據

  • 可經過內存提速,CPU佔有率低。

索引

索引類型

  • Primary Index,默認索引,創建字段是_key_id上,一個哈希索引
  • Edge Index,默認索引,創建在_from_to上,哈希索引;不能用於範圍查詢、排序,弱於OrientDB
  • Hash Index,自建
  • Skiplist Index,有序索引,
    • 用於快速查找具備特定屬性值的文檔,範圍查詢以及按索引排序順序返回文檔。
    • 用於查找,範圍查詢和排序。補全範圍查詢的缺點。

Primary IndexEdge Index,是內存索引,文檔加載速度很慢,推測是在重建索引。沒有見到ArangoDB說有內存索引持久化。


  • Persistent IndexRocksDB的索引。
    • 持久性索引是具備持久性的排序索引。當存儲或更新文檔時,索引條目將寫入磁盤。
    • 使用持久性索引可能會減小集合加載時間。
  • Geo Index,用戶能夠在集合中的一個或多個屬性上建立其餘地理索引。地理索引用於快速找到地球表面的地方。
  • Fulltext Index,全文索引

Hash查詢很快,幾乎爲$O(1)$。


Novel Hybrid Index

image.png

把全部的邊都存儲在一個大哈希表中,把每一個頂點V都放到一個雙鏈表中。

  • 將節點放入鏈表中,遍歷全部節點的複雜度時$O(k)$,k是節點總數。
    • 節點鄰接點是經過邊集肯定的,邊集的起始點和結束點都是默認Hash索引的,因此查找一個節點的時間複雜度是$O(1)$。
  • 遍歷邊的複雜度是$O(log E) + O(k)$,E是邊的數量。
    • 邊的遍歷是經過遍歷節點開始的。
    • 刪除和修改邊的時候,是經過節點的key查找的,確認邊是否是節點的鏈表中的第一個,不是就經過鏈表繼續找。
    • 邊索引不只是邊集的索引,也是頂點的鄰接點的索引。

存儲引擎

在ArangoDB 3.0 這個版本,arangodb切換了本身的存儲引擎,RocksDB

Persistent indexes via RocksDB is the first step of ArangoDB to persist indexes in general.

在docker下這個版本的ArangoDB的接口沒有作好,掛在存儲卷時會致使RocksDB IO異常。

架構變更很頻繁。3.2版本還會引入pregel框架。

  • 數據庫的Bug不但存在於自己還存在於其它引用的架構處。

OrientDB

索引類型

  • SB-Tree Index:從其餘索引類型中得到的特性的良好組合,默認索引
  • Hash Index:
  • Auto Sharding Index:提供一個DHT實現;不支持範圍查詢
  • Lucene Spatial Index:持久化,支持事物,範圍查詢

在 Java中,若是哈希函數不合理,返回值過於集中,會致使大字典更慢。Java 因爲存在鏈表和紅黑樹互換機制,搜索時間呈對數級增加,而非線性增加。在理想的哈希函數下,不管字典多大,搜索速度都是同樣快。


SB-Tree Index

  • UNIQUE:這些索引不容許重複的鍵。對於複合索引,這指的是組合鍵的唯一性。
  • NOTUNIQUE:這些索引容許重複的鍵。
  • FULLTEXT:這些索引是基於任何單個文本的。能夠經過CONTAINSTEXT操做符在查詢中使用它們。
  • DICTIONARY:這些索引相似於使用唯一的索引,可是在重複鍵的狀況下,它們用新的記錄替換現有的記錄。

Lucene Engine

  • FULLTEXT:這些索引使用Lucene引擎來索引字符串內容。能夠在LUCENE操做符的查詢中使用它們。
  • SPATIAL:這些索引使用Lucene引擎來索引地理空間座標。

SB索引

SB索引,B樹上優化了數據插入和範圍查詢,時間複雜度$O(log(N))$,其底數大約500。

  • 磁盤消耗大。

使用相似繼承的方式去實現包含特殊屬性的頂點集和邊集。


OrientDB本地存儲原則

OrientDB本地存儲原則:使用包含由固定大小部分(頁面)分割的磁盤數據並寫入日誌記錄方法的磁盤緩存(當頁面中的更改首先記錄在所謂的持久存儲器中時),咱們能夠實現如下特性:OrientDB 2.2.x——PLocal Engine

  • Operations on single page are atomic.
  • Changes applied to the page can be restored after server crash even if they were not flushed to the disk.

保護數據


image.png

集羣實現就是經過Class的相似繼承機制實現的分表。Clusters


內置查詢語言分析

AQL

arangodarangosh是用CPP寫的。

  • 網絡/磁盤IO處理也是經過CPP寫的
  • AQL執行器:CPP
  • AQL函數大部分是經過CPP寫的,少部分是經過JS寫的。
  • AQL調用的用戶函數全是JS函數

但,arangodarangosh依賴V8 JS引擎

  • 全部的JS命令行進入arangosh都會被V8執行。
  • arangodJS孤島,--javascript.v8-contexts,在多線程中用JS,可是JS自己仍是單線程的。

類SQL語言,與ES6無縫鏈接,可使用ES6語法。

  • JS的引入功能相似存儲過程,提供Foxx框架
  • 以V8做爲語句執行引擎,性能有問題,並且致使不少坑;
  • V8和Neo4J與OrientDB的JVM相比差距有點大,執行的性能感受和Node.JS差不錯,適合事物密集型而不適合計算密集型的程序。
  • 我的認爲,arangosh使用V8的目的是經過異步回掉調用本地cpp代碼提供計算性能,而不是使用V8去直接計算,因此在用各圖數據庫實現圖算法的時候若是使用JS去實現的話,性能會不是那麼的友善,對於ArangoDB值得期待的就是pregel將會在3.2版本面世。

Cypher

語義清晰,Neo4J惟一支持的語言

OrientDB SQL

類SQL,語法和SQL基本相似,冗長。


性能分析

少許數據分析

億級數據分析

ArangoDB

arangoimp插入效率感人,推測緣由:

  • 導入方式是邊插入邊創建索引的,可能性不高,由於同一數據集在多臺主機上嚴重了其卡住的位置是不一樣的
  • 其hash函數設置很差,致使不停的哈希衝突,hash索引是arangodb的默認索引,可能性也不高;
  • 官網上有模糊的說明,arangodb的索引是存儲在內存中的,官網特地說明Persistent Index這個索引是存儲在磁盤上的,其餘索引是須要在文檔加載時候從新創建索引的。

arangoimp在默認狀況下到達1300萬數據以後導入性能不好。


在都沒有支持複雜圖算法的狀況下,十萬級數據ArangoDB的圖計算效率比較低,由於是單線程JS在V8上運行的。
arangodb對於邊的插入不支持批量插入:

  • arangosh中已經驗證,其只能一條邊一條邊的插入,後面的數據會被無視掉。

arangoimp上存在無效參數:

  • 好比建立邊集選項,不管是否選擇是true,都不會建立,Github上官方解釋必須在數據庫中先建立邊集才能夠,也就是說這個命令中的建立邊集的參數是一個無效參數。

Neo4J

neo4j-import導入數據很快。

root@ubuntu:/var/lib/neo4j/data/databases# neo4j-import --into njaq --nodes /home/dawn/csv/perosnInfo.csv --relationships /home/dawn/csv/know.csv --skip-bad-relationships true --skip-bad-entries-logging true --bad-tolerance true
WARNING: neo4j-import is deprecated and support for it will be removed in a future
version of Neo4j; please use neo4j-admin import instead.

Neo4j version: 3.2.1
Importing the contents of these files into njaq:
Nodes:
  /home/dawn/csv/perosnInfo.csv
Relationships:
  /home/dawn/csv/know.csv

Available resources:
  Total machine memory: 3.84 GB
  Free machine memory: 1.61 GB
  Max heap memory : 875.00 MB
  Processors: 4
  Configured max memory: 700.35 MB

Nodes, started 2017-06-08 05:35:30.741+0000
[>:18.87 MB|NODE:152.59 MB----|*PROPERTIES(3)============|LABEL SCAN--|v:37.14 MB/s-----------]20.0M ∆21.8K
Done in 51s 548ms
Prepare node index, started 2017-06-08 05:36:22.495+0000
[*DETECT:419.62 MB----------------------------------------------------------------------------]20.0M ∆-6500000
Done in 9s 126ms
Relationships, started 2017-06-08 05:36:31.678+0000
[>:7|T|*PREPARE(4)=========================================================|RE|CALCULATE-|P|v:]79.9M ∆10.9K
Done in 4m 17s 742ms
Relationship --> Relationship  1/1, started 2017-06-08 05:40:49.548+0000
[*>-----------------------------------------------------------------------|LINK------------|v:]79.9M ∆ 405K
Done in 2m 5s 784ms
RelationshipGroup 1/1, started 2017-06-08 05:42:55.404+0000
[*>:??----------------------------------------------------------------------------------------]    00
Done in 11ms
Node --> Relationship, started 2017-06-08 05:42:55.439+0000
[>:13|*>-------------------------------------------------|LIN|v:26.00 MB/s--------------------]19.9M ∆2.18M Done in 11s 833ms Relationship <-- Relationship 1/1, started 2017-06-08 05:43:07.308+0000 [*>-------------------------------------------------------------------------------|LINK----|v:]79.9M ∆ 168K Done in 11m 29s 787ms Count groups, started 2017-06-08 05:54:37.570+0000 [*>:??----------------------------------------------------------------------------------------] 0 ∆ 0 Done in 1ms Gather, started 2017-06-08 05:54:38.061+0000 [*>:??----------------------------------------------------------------------------------------] 0 ∆ 0 Done in 4ms Write, started 2017-06-08 05:54:38.156+0000 [*>:??----------------------------------------------------------------------------------------] 0 ∆ 0 Done in 15ms Node --> Group, started 2017-06-08 05:54:38.213+0000 [*>:??----------------------------------------------------------------------------------------] 0 ∆ 0 Done in Node counts, started 2017-06-08 05:54:38.264+0000 [*>(4)====================================================================================|COU]20.0M ∆80.0K
Done in 1m 26s 338ms
Relationship counts, started 2017-06-08 05:56:04.625+0000
[*>(4)======================================================|COUNT----------------------------]80.0M ∆1.81M Done in 2m 47s 277ms IMPORT DONE in 23m 22s 420ms. Imported: 20000000 nodes 79994052 relationships 80000000 properties Peak memory usage: 899.62 MB 

Neo4J使用導入方法以後會創建索引,不然基本沒有性能,創建索引很快。


圖算法支持

ArangoDB圖算法支持

  1. AQL
    1. 遍歷:從指定開始點,經過必定算法、邊類型、圖類型、深度獲取與指定開始點相關連通的點。
      1. 數據源:圖、邊集合
      2. 邊方向:出邊、入邊、所有
      3. 遍歷方式:BFS Or DFS
    2. 最短路徑:兩點最短路徑,選項基本和上面類型
  2. Pregel
    1. @arangodb/pregel 文件夾下,不少分佈式的圖算法
    2. PageRank
    3. CC 強弱連通算法
    4. 單源最短路徑算法

JS擴展

  • 經過JS能夠完成對內置算法的擴展,可是自定義方法是單線程JS函數,若是用來作算法,性能堪憂,最佳選擇就是選擇內置的方法去實現圖算法。
  • 經過JS能夠實現不少算法,可是在ArangoSH下代碼單線程運做,雖然arangod的JS是在多線程中運行的,可是arangosh是在單線程中運行的,且JS自己並不擅長處理計算型代碼,相比之下經過內置的數據庫語言而不是這種內置語言與JS混雜方式的代碼會快不少;好比Neo4J,OrientDB的查詢語言。

Neo4J

對於普通的遍歷最短路徑算法支持和ArangoDB同樣都支持,但Neo4J的圖的遍歷深度的閾值設置比較難,且深度超過6算法會效率比較低。

相比之下,ArangoDB的算法參數設置所有依賴於Key-Value實現,算法在編碼層次靈活性很高。

對於PageRank,CC等算法的實現,Neo4J提供兩種方式:

  • 編寫Jar包,GitHub上有個未被官方認可的Jar包
  • Cypher直接實現

OrientDB

同上,圖算法也支持Jar包導入。


內置圖算法

最短路徑

Cypher:

match (p1:person{no:'%s'}),(p2:person{no:'%s'}) match p=shortestPath((p1)-[*..3]->(p2)) return p

OrientDB SQL:

select dijkstra((select @RID from persons where no='%s'),(select @RID from persons where no='%s'),'E')

AQL:

for v,e in outbound shortest_path '%s' to '%s' graph 'graphPersons' return [v._key,e._key]

鄰接點

Cypher:

MATCH (js:person)-[:know]-(surfer) WHERE js.no = '%s' return surfer

OrientDB SQL:

select from E where out = (select @RID from persons where no='%s 

AQL:

traversal_results = graphPersons.traverse(
      start_vertex='persons/'+getSingleInfo(id).no,
      strategy='bfs',
      direction='outbound',
      edge_uniqueness='global',
      vertex_uniqueness='global',
      max_depth=1
  )

參考資料

原文地址:https://www.jianshu.com/p/6cab7a150755
相關文章
相關標籤/搜索