天天看點

圖形資料庫、NOSQL和Neo4j(轉載)

源位址:http://www.infoq.com/cn/articles/graph-nosql-neo4j

簡介

在衆多不同的資料模型裡,關系資料模型自80年代就處于統治地位,而且有不少實作,如Oracle、MySQL和MSSQL,它們也被稱為關系資料庫管理系統(RDBMS)。然而,最近随着關系資料庫使用案例的不斷增加,一些問題也暴露了出來,這主要是因為兩個原因:資料模組化中的一些缺陷和問題,以及在大資料量和多伺服器之上進行水準伸縮的限制。兩個趨勢讓這些問題引起了全球軟體社群的重視:

  1. 使用者、系統和傳感器産生的資料量呈指數增長,其增長速度因大部分資料量集中在象Amazon、Google和其他雲服務這樣的分布式系統上而進一步加快。
  2. 資料内部依賴和複雜度的增加,這一問題因網際網路、Web2.0、社交網絡,以及對大量不同系統的資料源開放和标準化的通路而加劇。

在應對這些趨勢時,關系資料庫産生了更多的問題。這導緻大量解決這些問題某些特定方面的不同技術的出現,它們可以與現有RDBMS互相配合或代替它們 - 亦被稱為混合持久化(Polyglot Persistence)。資料庫替代品并不是新鮮事物,它們已經以對象資料庫(OODBMS)、層次資料庫(如LDAP)等形式存在很長時間了。但是,過去幾年間,出現了大量新項目,它們被統稱為NOSQL資料庫(NOSQL-databases)

本文旨在介紹圖形資料庫(Graph Database)在NOSQL運動裡的地位,第二部分則是對Neo4j(一種基于Java的圖形資料庫)的簡介。

NOSQL環境

NOSQL(Not Only SQL,不限于SQL)是一類範圍非常廣泛的持久化解決方案,它們不遵循關系資料庫模型,也不使用SQL作為查詢語言。

簡單地講,NOSQL資料庫可以按照它們的資料模型分成4類:

  1. 鍵-值存儲庫(Key-Value-stores)
  2. BigTable實作(BigTable-implementations)
  3. 文檔庫(Document-stores)
  4. 圖形資料庫(Graph Database)

就Voldemort或Tokyo Cabinet這類鍵/值系統而言,最小的模組化單元是鍵-值對。對BigTable的克隆品來講,最小模組化單元則是包含不同個數屬性的元組,至于象CouchDB和MongoDB這樣的文檔庫,最小單元是文檔。圖形資料庫則幹脆把整個資料集模組化成一個大型稠密的網絡結構。

在此,讓我們深入檢閱NOSQL資料庫的兩個有意思的方面:伸縮性和複雜度。

1. 伸縮性

CAP: ACID vs. BASE

為了保證資料完整性,大多數經典資料庫系統都是以事務為基礎的。這全方位保證了資料管理中資料的一緻性。這些事務特性也被稱為

ACID

A

代表原子性、

C

表示一緻性、

I

是隔離性、

D

則為持久性)。然而,ACID相容系統的向外擴充已經表現為一個問題。在分布式系統中,高可用性不同方面之間産生的沖突沒有完全得到解決 - 亦稱

CAP

法則:

  • 一緻性(C) :所有用戶端看到的資料是同一個版本,即使是資料集發生了更新 - 如利用兩階段送出協定(XA事務),和ACID,
  • 可用性(A) :所有用戶端總能找到所請求資料的至少一個版本,即使叢集中某些機器已經當機,
  • 分區容忍性(P) :整個系統保持自己的特征,即使是被部署到不同伺服器上的時候,這對用戶端來講是透明的。

CAP法則假定向外擴充的3個不同方面中隻有兩個可以同時完全實作。

圖形資料庫、NOSQL和Neo4j(轉載)

為了能處理大型分布式系統,讓我們深入了解所采用的不同CAP特征。

很多NOSQL資料庫首先已經放寬了對于

一緻性(C)

的要求,以期得到更好的

可用性(A)

分區容忍性(P)

。這産生了被稱為

BASE

基本(B)可用性(A)

軟狀态(S)

最終一緻性(E)

)的系統。它們沒有經典意義上的事務,并且在資料模型上引入了限制,以支援更好的分區模式(如Dynamo系統等)。關于CAP、ACID和BASE的更深入讨論可以在這篇介紹裡找到。

2. 複雜度

圖形資料庫、NOSQL和Neo4j(轉載)
蛋白質同源網絡(Protein Homology Network),感謝Alex Adai:細胞和分子生物學院 - 德州大學

資料和系統的互聯性增加産生了一種無法用簡單明了或領域無關(domain-independent)方式進行伸縮和自動分區的稠密資料集,甚至連Todd Hoff也提到了這一問題。關于大型複雜資料集的可視化内容可以通路可視化複雜度(Visual Complexity)。

關系模型

在把關系資料模型扔進故紙堆之前,我們不應該忘記關系資料庫系統成功的一個原因是遵照E.F. Codd的想法,關系資料模型通過規範化的手段原則上能夠模組化任何資料結構且沒有資訊備援和丢失。模組化完成之後,就可以使用SQL以一種非常強大的方式插入、修改和查詢資料。甚至有些資料庫,為了插入速度或針對不同使用情況(如OLTP、OLAP、Web應用或報表)的多元查詢(星形模式),對模式實作了優化。

這隻是理論。然而在實踐中,RDBM遇到了前面提到的CAP問題的限制,以及由高性能查詢實作而産生的問題:聯結大量表、深度嵌套的SQL查詢。其他問題包括伸縮性、随時間的模式演變,樹形結構的模組化,半結構化資料,層級和網絡等。

關系模型也很難适應目前軟體開發的方法,如面向對象和動态語言,這被稱為對象-關系阻抗失配。由此,象Java的Hibernate這樣的ORM層被開發了出來,而且被應用到這種混合環境裡。它們固然簡化了把對象模型映射到關系資料模型的任務,但是沒有優化查詢的性能。尤其是半結構化資料往往被模組化成具有許多列的大型表,其中很多行的許多列是空的(稀疏表),這導緻了拙劣的性能。甚至作為替代方法,把這些結構模組化成大量的聯結表,也有問題。因為RDBMS中的聯結是一種非常昂貴的集合操作。

圖形是關系規範化的一種替代技術

看看領域模型在資料結構上的方案,有兩個主流學派 - RDBMS采用的關系方法和圖 - 即網絡結構,如語義網用到的。

盡管圖結構在理論上甚至可以用RDBMS規範化,但由于關系資料庫的實作特點,對于象檔案樹這樣的遞歸結構和象社交圖這樣的網絡結構有嚴重的查詢性能影響。網絡關系上的每次操作都會導緻RDBMS上的一次"聯結"操作,以兩個表的主鍵集合間的集合操作來實作 ,這種操作不僅緩慢并且無法随着這些表中元組數量的增加而伸縮。

圖形資料庫、NOSQL和Neo4j(轉載)
屬性圖形(Property Graph)的基本術語

在圖的領域,并沒有一套被廣泛接受的術語,存在着很多不同類型的圖模型。但是,有人緻力于建立一種屬性圖形模型(Property Graph Model),以期統一大多數不同的圖實作。按照該模型,屬性圖裡資訊的模組化使用3種構造單元:

  • 節點 (即頂點)
  • 關系 (即邊) - 具有方向和類型(标記和标向)
  • 節點和關系上面的 屬性 (即特性)

更特殊的是,這個模型是一個被标記和标向的屬性多重圖(multigraph)。被标記的圖每條邊都有一個标簽,它被用來作為那條邊的類型。有向圖允許邊有一個固定的方向,從末或源節點到首或目标節點。屬性圖允許每個節點和邊有一組可變的屬性清單,其中的屬性是關聯某個名字的值,簡化了圖形結構。多重圖允許兩個節點之間存在多條邊。這意味着兩個節點可以由不同邊連接配接多次,即使兩條邊有相同的尾、頭和标記。

下圖顯示了一個被标記的小型屬性圖。

圖形資料庫、NOSQL和Neo4j(轉載)
TinkerPop有關的小型人員圖

圖論的巨大用途被得到了認可,它跟不同領域的很多問題都有關聯。最常用的圖論算法包括各種類型的最短路徑計算、測地線(Geodesic Path)、集中度測量(如PageRank、特征向量集中度、親密度、關系度、HITS等)。然而,在很多情況下,這些算法的應用僅限制于研究,因為實際中沒有任何可用于産品環境下的高性能圖形資料庫實作。幸運的是,近些年情況有所改觀。有幾個項目已經被開發出來,而且目标直指24/7的産品環境:

  • Neo4j - 開源的Java屬性圖形模型
  • AllegroGraph,閉源,RDF-QuadStore
  • Sones - 閉源,關注于.NET
  • Virtuoso - 閉源,關注于RDF
  • HyergraphDB - 開源的Java超圖模型
  • Others like InfoGrid、Filament、FlockDB等。

下圖展示了在複雜度和伸縮性方面背景下的主要NOSQL分類的位置。

圖形資料庫、NOSQL和Neo4j(轉載)

關于“規模擴充和複雜度擴充的比較”的更多内容,請閱讀Emil Eifrem的博文。

Neo4j - 基于Java的圖形資料庫

Neo4j是一個用Java實作、完全相容ACID的圖形資料庫。資料以一種針對圖形網絡進行過優化的格式儲存在磁盤上。Neo4j的核心是一種極快的圖形引擎,具有資料庫産品期望的所有特性,如恢複、兩階段送出、符合XA等。自2003年起,Neo4j就已經被作為24/7的産品使用。該項目剛剛釋出了1.0版 - 關于伸縮性和社群測試的一個主要裡程碑。通過聯機備份實作的高可用性和主從複制目前處于測試階段,預計在下一版本中釋出。Neo4j既可作為無需任何管理開銷的内嵌資料庫使用;也可以作為單獨的伺服器使用,在這種使用場景下,它提供了廣泛使用的REST接口,能夠友善地內建到基于PHP、.NET和JavaScript的環境裡。但本文的重點主要在于讨論Neo4j的直接使用。

開發者可以通過Java-API直接與圖形模型互動,這個API暴露了非常靈活的資料結構。至于象JRuby/Ruby、Scala、Python、Clojure等其他語言,社群也貢獻了優秀的綁定庫。Neo4j的典型資料特征:

  • 資料結構不是必須的,甚至可以完全沒有,這可以簡化模式變更和延遲資料遷移。
  • 可以友善模組化常見的複雜領域資料集,如CMS裡的通路控制可被模組化成細粒度的通路控制表,類對象資料庫的用例、TripleStores以及其他例子。
  • 典型使用的領域如語義網和RDF、LinkedData、GIS、基因分析、社交網絡資料模組化、深度推薦算法以及其他領域。

甚至“傳統”RDBMS應用往往也會包含一些具有挑戰性、非常适合用圖來處理的資料集,如檔案夾結構、産品配置、産品組裝和分類、媒體中繼資料、金融領域的語義交易和欺詐檢測等。

圍繞核心,Neo4j提供了一組可選的元件。其中有支援通過元模型構造圖形結構、SAIL - 一種SparQL相容的RDF TripleStore實作或一組公共圖形算法的實作。

要是你想将Neo4j作為單獨的伺服器運作,還可以找到REST包裝器。這非常适合使用LAMP軟體搭建的架構。通過memcached、e-tag和基于Apache的緩存和Web層,REST甚至簡化了大規模讀負荷的伸縮。

高性能?

要給出确切的性能基準資料很難,因為它們跟底層的硬體、使用的資料集和其他因素關聯很大。自适應規模的Neo4j無需任何額外的工作便可以處理包含數十億節點、關系和屬性的圖。它的讀性能可以很輕松地實作每毫秒(大約每秒1-2百萬周遊步驟)周遊2000關系,這完全是事務性的,每個線程都有熱緩存。使用最短路徑計算,Neo4j在處理包含數千個節點的小型圖時,甚至比MySQL快1000倍,随着圖規模的增加,差距也越來越大。

這其中的原因在于,在Neo4j裡,

圖周遊執行的速度是常數

,跟圖的規模大小無關。

不象在RDBMS裡常見的聯結操作那樣,這裡不涉及降低性能的集合操作

。Neo4j以一種延遲風格周遊圖 - 節點和關系隻有在結果疊代器需要通路它們的時候才會被周遊并傳回,對于大規模深度周遊而言,這極大地提高了性能。

寫速度跟檔案系統的查找時間和硬體有很大關系。Ext3檔案系統和SSD磁盤是不錯的組合,這會導緻每秒大約100,000寫事務操作。

示例 - 黑客帝國

前面已經說過,社交網絡隻是代表了圖形資料庫應用的冰山一角,但用它們來作為例子可以讓人很容易了解。為了闡述Neo4j的基本功能,下面這個小型圖來自黑客帝國這部電影。該圖是用Neo4j的Neoclipse産生的,該插件基于Eclipse RCP:

圖形資料庫、NOSQL和Neo4j(轉載)

這個圖連結到一個已知的引用節點(id=0),這是為了友善的從一個已知起點找到條路進入這個網絡。這個節點不是必須的,但實踐證明它非常有用。

Java的實作看上去大概是這個樣子:

在“target/neo”目錄建立一個新的圖形資料庫

EmbeddedGraphDatabase graphdb = new EmbeddedGraphDatabase("target/neo");      

關系類型可以動态建立:

RelationshipType KNOWS = DynamicRelationshipType.withName("KNOWS");      

或通過類型安全的Java Enum:

enum Relationships implements RelationshipType { KNOWS, INLOVE, HAS_CODED, MATRIX }      

現在,建立2個節點,給每個節點加上“name”屬性。接着,把兩個節點用一個“KNOWS”關系聯系起來:

Node neo = graphdb.createNode();node.setProperty("name", "Neo");Node morpheus = graphdb.createNode();morpheus.setProperty("name", "Morpheus");neo.createRelationshipTo(morpheus, KNOWS);      

任何修改圖或需要資料隔離級别的操作要包在事務中,這樣可以利用内置的復原和恢複功能:

Transaction tx = graphdb.beginTx();try {Node neo = graphdb.createNode();...tx.success();} catch (Exception e) {tx.failure();} finally {tx.finish();}      

建立“黑客帝國”圖的完整代碼:

graphdb = new EmbeddedGraphDatabase("target/neo4j");index = new LuceneIndexService(graphdb);Transaction tx = graphdb.beginTx();try {Node root = graphdb.getReferenceNode();// we connect Neo with the root node, to gain an entry point to the graph// not neccessary but practical.neo = createAndConnectNode("Neo", root, MATRIX);Node morpheus = createAndConnectNode("Morpheus", neo, KNOWS);Node cypher = createAndConnectNode("Cypher", morpheus, KNOWS);Node trinity = createAndConnectNode("Trinity", morpheus, KNOWS);Node agentSmith = createAndConnectNode("Agent Smith", cypher, KNOWS);architect = createAndConnectNode("The Architect", agentSmith, HAS_CODED);// Trinity loves Neo. But he doesn't know.trinity.createRelationshipTo(neo, LOVES);tx.success();} catch (Exception e) {tx.failure();} finally {tx.finish();}      

以及建立節點和關系的成員函數

private Node createAndConnectNode(String name, Node otherNode,RelationshipType relationshipType) {    Node node = graphdb.createNode();    node.setProperty("name", name);    node.createRelationshipTo(otherNode, relationshipType);    index.index(node, "name", name);    return node;}      

誰是Neo的朋友?

Neo4j的API有一組面向Java集合的方法可輕易地完成查詢。這裡,隻消看看“Neo”節點的關系便足以找出他的朋友:

for (Relationship rel : neo.getRelationships(KNOWS)) {    Node friend = rel.getOtherNode(neo);    System.out.println(friend.getProperty("name"));}returns "Morpheus" as the only friend.      

但是,Neo4j的真正威力源自Traverser-API的使用,它可以完成非常複雜的周遊描述和過濾器。它由Traverser和ReturnableEvaluator組成,前者計算StopEvaluator來獲知何時停止,後者則用于在結果中包含哪些節點。此外,你還可以指定要周遊關系的類型和方向。Traverser實作了Java的Iterator接口,負責延遲加載和周遊整個圖,在節點被首次要求通路(如for{...}循環)時進行。它還内置了一些常用的Evaluator和預設值:

Traverser friends = neo.traverse(Order.BREADTH_FIRST,StopEvaluator.DEPTH_ONE,ReturnableEvaluator.ALL_BUT_START_NODE, KNOWS, Direction.BOTH);for (Node friend : friends) {    System.out.println(friend.getProperty("name"));}      

我們在繼續通路更深一級的節點之前首先從起點通路處于同一深度的所有節點(Order.BREADTH_FIRST),在深度為1的一次周遊後停止(StopEvaluator.DEPTH_ONE),然後傳回除了起點("Neo")之外的所有節點(ReturnableEvaluator.ALL_BUT_START_NODE)。我們在兩個方向隻周遊類型為KNOWS的關系。這個周遊器再次傳回Morpheus是Neo僅有的直接朋友。

朋友的朋友?

為了調查誰是Neo朋友的朋友,KNOWS網絡需要再進行深度為2的步驟,由Neo開始,傳回Trinity和Cypher。實際程式設計中,這可以通過調整我們的Traverser的StopEvaluator,限制周遊深度為2來實作:

StopEvaluator twoSteps = new StopEvaluator() {    @Override    public boolean isStopNode(TraversalPosition position) {        return position.depth() == 2;    }};      

還要定制ReturnableEvaluator,隻傳回在深度2找到的節點:

ReturnableEvaluator nodesAtDepthTwo = new ReturnableEvaluator() {    @Override    public boolean isReturnableNode(TraversalPosition position) {        return position.depth() == 2;    }};      

現在“朋友的朋友”周遊器就成了:

Traverser friendsOfFriends = neo.traverse(Order.BREADTH_FIRST,    twoSteps, nodesAtDepthTwo, KNOWS, Direction.BOTH);for (Node friend : friendsOfFriends) {    System.out.println(friend.getProperty("name"));}      

它的結果是Cypher和Trinity。

誰在戀愛?

另一個有趣的問題是,這個圖上是否有人正在熱戀,比方說從架構師(Architect)開始。

這次,整個圖需要沿着由架構師(假定他的節點ID是已知的,但要到很晚才知道)開始的任何關系開始檢查,傳回擁有向外LOVE關系的節點。一個定制的ReturnableEvaluator可以完成這件事:

ReturnableEvaluator findLove = new ReturnableEvaluator() {@Override    public boolean isReturnableNode(TraversalPosition position) {        return position.currentNode().hasRelationship(LOVES, Direction.OUTGOING);    }};      

為了周遊所有關系,需要知道整個圖的所有關系類型:

List<Object> types = new ArrayList<Object>();// we have to consider all relationship types of the whole graph// (in both directions)for(RelationshipType type : graphdb.getRelationshipTypes()) {    types.add(type);    types.add(Direction.BOTH);}//let's go!Traverser inLove = architect.traverse(Order.BREADTH_FIRST,StopEvaluator.END_OF_GRAPH, findLove, types.toArray());for (Node lover : inLove) {    System.out.println(lover.getProperty("name"));}      

上述代碼的傳回結果隻有一個節點:Trinity,因為我們隻傳回擁有向外LOVE關系的節點。

給圖建立索引

盡管沿着所有關系的周遊操作是Neo4j的亮點之一,但也需要在整個圖之上進行面向集合的操作。所有節點屬性的全文檢索就是一個典型的例子。為了不重新發明輪子,Neo4j在這裡使用了外部索引系統。針對常見的基于文本的搜尋,Neo4j已經跟Lucene和Solr進行了深度內建,在Lucene/Solr裡增加了給具有事務語義的任意節點屬性建立索引的功能。

在黑客帝國的例子裡,如給“name”屬性建立索引:

GraphDatabaseService graphDb = // a GraphDatabaseService instanceIndexService index = new LuceneIndexService( graphDb );//create a new node and index the "name" propertyNode neo = graphDb.createNode();neo.setProperty( "name", "Neo" );index.index( neo, "name", neo.getProperty( "name" ) );//search for the first node with "name=Neo"Node node = index.getSingleNode( "name", "Neo" );      

Lucene是圖的外部索引的一個例子。但是,作為一個快速圖形引擎,有大量的政策來建構圖本身内部的索引結構,針對特殊資料集和領域縮短周遊模式。例如,有針對一維資料的timeline和B樹,給二維資料(在空間和GIS社群非常普遍)建立索引的RTrees和QuadTrees等。另一個常見的有用模式是将重要的子圖直接連接配接到根節點,以建立重要開始節點的快捷路徑。

Java太麻煩了。拜托,有沒有簡短點的?

Neo4j甚至還提供了一組優秀的語言綁定來簡化圖結構操作。這個例子使用了優秀的Neo4j-JRuby-綁定,它極大地減少了整個代碼量:

先安裝neo4j gem

>gem install neo4j      

這時,整個黑客帝國的圖和前面提到的查詢,用JRuby代碼編寫就成了這個樣子:

require "rubygems"require "neo4j"class Person  include Neo4j::NodeMixin  #the properties on the nodes  property :name  #the relationship types  has_n :friends  # Lucene index for some properties  index :nameend#the playersneo       = Person.new :name => 'Neo'morpheus  = Person.new :name => 'Morpheus'trinity   = Person.new :name => 'Trinity'cypher    = Person.new :name => 'Cypher'smith     = Person.new :name => 'Agent Smith'architect = Person.new :name => 'Architect'#the connectionscypher.friends << morpheuscypher.friends << smithneo.friends << morpheusmorpheus.friends << trinitytrinity.rels.outgoing(:loves) << neoarchitect.rels.outgoing(:has_coded) << smith#Neos friendsneo.friends.each { |n| puts n }#Neos friends-of-friendsneo.friends.depth(2).each { |n| puts n }#Who is in love?architect.traverse.both(:friends, :has_coded, :loves).depth(:all).filter do  outgoing(:loves).to_a.size > 0end.each do |n| puts 'In love: ' + n.nameend      

圖程式設計語言 - Gremlin

圖形資料庫、NOSQL和Neo4j(轉載)

直到最近,還沒有任何查詢語言涉及大型的圖領域和圖相關項目。在語義網/RDF領域,有SPARQL,受SQL啟發的查詢語言,專注于描述用來比對元組集合的樣本圖。但是,大量的圖并不相容RDF,而且采用不同或更側重于更實用的方式進行資料模組化,象本文中的黑客帝國例子,以及其他領域特定的資料集。其他查詢語言都是面向JSON的,如MQL,一種用于Freebase的查詢語言。這些語言隻工作于它們自己定義的資料模型,完全不支援或隻非常有限地支援深度圖算法和啟發式分析方法,而這又是當今大型圖裡不可或缺的内容。

至于針對各種圖資料模型(如RDF)的更複雜有趣的查詢,Gremlin - 一種面向XPath,圖靈完備的圖形程式設計語言 - 正由TinkerPop團隊開發,主要由Marko A. Rodriguez推動。借助引入屬性圖模型,它創造了一個針對現有大多數模型的超集,以及最小的公共操作集合。此外,它允許連接配接其他圖形架構(如Gremlin使用JUNG),同時支援在不同的圖實作上都能表達圖的周遊。已支援的一組實作包括,從簡單的如記憶體中的TinkerGraph,到其他通過針對AllegroGraph、Sesame和ThinkerPop LinkedData SAIL(最開始由Josh Shinavier為Ripple程式設計語言開發)的RDF-SAIL擴充卡,一直到Neo4j。

Gremlin的文法建立在XPath基礎之上,這是為了可以簡單地表達整個圖的深度路徑描述。很多簡單的例子幾乎就像普通的XPath。

在安裝Gremlin或線上試用之後,黑客帝國例子裡的圖的Gremlin會話大緻是:

peterneubauer$ ~/code/gremlin/gremlin.sh         \,,,/         (o o)-----oOOo-(_)-oOOo-----gremlin> #open a new neo4j graph as the default graph ($_g)gremlin> $_g := neo4j:open('tmp/matrix')==>neo4jgraph[tmp/matrix]gremlin> #the verticesgremlin> $neo := g:add-v(g:map('name','Neo'))==>v[1]gremlin> $morpheus := g:add-v(g:map('name','Morpheus'))==>v[2]gremlin> $trinity := g:add-v(g:map('name','Trinity'))==>v[3]gremlin> $cypher := g:add-v(g:map('name','Cypher'))==>v[4]gremlin> $smith := g:add-v(g:map('name','Agent Smith'))==>v[5]gremlin> $architect := g:add-v(g:map('name','The Architect'))==>v[6]gremlin> #the edgesgremlin> g:list($cypher,$neo,$trinity)[g:add-e($morpheus,'KNOWS',.)]==>v[4]==>v[1]==>v[3]gremlin> g:add-e($cypher,'KNOWS',$smith)==>e[3][4-KNOWS->5]gremlin> g:add-e($trinity,'LOVES',$neo)==>e[4][3-LOVES->1]gremlin> g:add-e($architect,'HAS_CODED',$smith)==>e[5][6-HAS_CODED->5]gremlin> #go to Neo as the current root ($_) via a full-text index searchgremlin> $_ := g:key('name','Neo')==>v[1]gremlin> #is this Neo?gremlin> ./@name==>Neogremlin> #what links to here and from here?gremlin> ./bothE==>e[0][1-KNOWS->2]==>e[4][3-LOVES->1]gremlin> #only take the KNOWS-edgesgremlin> ./bothE[@label='KNOWS']==>e[0][1-KNOWS->2]gremlin> #Neo's friend's namesgremlin> ./bothE[@label='KNOWS']/inV/@name==>Morpheusgremlin>gremlin> #Neo's Friends of friends, 2 stepsgremlin> repeat 2               $_ := ./outE[@label='KNOWS']/inV           end==>v[4]==>v[3]gremlin> #What are their names?gremlin> ./@name==>Cypher==>Trinitygremlin> #every node in the whole graph with an outgoing LOVES edgegremlin> $_g/V/outE[@label='LOVES']/../@name==>Trinity      

深度圖算法 - 關系的價值

鑒于Gremlin的能力,黑客帝國的例子顯得相當幼稚。更有趣的是開發和測試大型圖上的算法。象特征向量集中度和Dijkstra這類窮舉算法并不會擴充到這些圖上,因為它們需要了解網絡裡的每個頂點。對于針對這些問題的如基于文法的随機通路器和擴散激活(Marko Rodriguez和這裡有更深入的解釋)這類概念,啟發式方法更合适。Google PageRank算法就是啟發式的,而且可以用Gremlin模組化,代碼如下(Greatful Dead的歌曲、演唱會和相冊圖的一個例子,圖從這裡裝入,2500個循環,每次重複能量損失15%):

圖形資料庫、NOSQL和Neo4j(轉載)
$_g := tg:open()g:load('data/graph-example-2.xml')$m := g:map()$_ := g:key('type', 'song')[g:rand-nat()]repeat 2500  $_ := ./outE[@label='followed_by'][g:rand-nat()]/inV  if count($_) > 0    g:op-value('+',$m,$_[1]/@name, 1.0)  end  if g:rand-real() > 0.85 or count($_) = 0    $_ := g:key('type', 'song')[g:rand-nat()]  endendg:sort($m,'value',true())      

它傳回如下的歌曲權重清單:

==>DRUMS=44.0==>PLAYING IN THE BAND=38.0==>ME AND MY UNCLE=32.0==>TRUCKING=31.0==>CUMBERLAND BLUES=29.0==>PROMISED LAND=29.0==>THE MUSIC NEVER STOPPED=29.0==>CASSIDY=26.0==>DARK STAR=26.0==>NOT FADE AWAY=26.0==>CHINA CAT SUNFLOWER=25.0==>JACK STRAW=25.0==>TOUCH OF GREY=24.0==>BEAT IT ON DOWN THE LINE=23.0==>BERTHA=23.0      

其底層圖的另一個有趣的例子是LinkedData圖,在網際網路上可線上了解:LinkedData和DBPedia的音樂推薦算法。

總結

就像RDBMS和其他持久化解決方案一樣,圖不是解決所有問題的銀彈。資料才是最重要的東西,它是關鍵,然後是查詢類型、要處理的操作結構,以及什麼需求要考慮伸縮性和CAP。

将采用NOSQL資料庫的高伸縮性方面作為使用非關系解決方案的唯一考量往往是不必要的,而且也不符合預期。使用Neo4j和當今的硬體,大多數情況下,完全可以在單執行個體裡容納整個領域模型和查詢數十億領域對象。

如果那還不夠,總是有辦法引入針對領域優化過的資料分區概念,無需引入文檔或鍵/值系統的硬資料模組化限制。最終導緻的結果是一個文檔模型、或領域特定的“對象資料庫”還是其他模型則取決于領域上下文和應用場景。

本文的代碼可以從這裡下載下傳。

我要感謝Michael Hunger和Marko Rodriguez給我提供了優秀的回報和有幫助的建議。

作者簡介

Peter Neubauer是Neo Technology的COO,他同時還是幾個基于Java和圖開源項目的合夥創辦人,如Neo4j、Gremlin、LinkedProcess、OPS4J、Qi4j。你可以通過[email protected].聯系到Peter。

轉載于:https://www.cnblogs.com/licheng/archive/2010/09/09/1822105.html