天天看點

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

  最近我在用圖形資料庫來完成對一個初創項目的支援。在使用過程中覺得這種圖形資料庫實際上挺有意思的。是以在這裡給大家做一個簡單的介紹。

NoSQL資料庫相信大家都聽說過。它們常常可以用來處理傳統的關系型資料庫所難以解決的一系列問題。通常情況下,這些NoSQL資料庫分為Graph,Document,Column Family以及Key-Value Store等四種。這四種類型的資料庫分别使用了不同的資料結構來記錄資料。是以它們所适用的場景也不盡相同。

  其中最為特别的便是圖形資料庫了。可以說,它和其它的一系列NoSQL資料庫非常不同:豐富的關系表示,完整的事務支援,卻沒有一個純正的橫向擴充解決方案。

  在本文中,我們就将對業界非常流行的圖形資料庫Neo4J進行簡單的介紹。

圖形資料庫簡介

  相信您和我一樣,在使用關系型資料庫時常常會遇到一系列非常複雜的設計問題。例如一部電影中的各個演員常常有主角配角之分,還要有導演,特效等人員的參與。通常情況下這些人員常常都被抽象為Person類型,對應着同一個資料庫表。同時一位導演本身也可以是其它電影或者電視劇的演員,更可能是歌手,甚至是某些影視公司的投資者(沒錯,我這個例子的确是以趙薇為模闆的)。而這些影視公司則常常是一系列電影,電視劇的資方。這種彼此關聯的關系常常會非常複雜,而且在兩個實體之間常常同時存在着多個不同的關系:

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

  在嘗試使用關系型資料庫對這些關系進行模組化時,我們首先需要建立表示各種實體的一系清單:表示人的表,表示電影的表,表示電視劇的表,表示影視公司的表等等。這些表常常需要通過一系列關聯表将它們關聯起來:通過這些關聯表來記錄一個人到底參演過哪些電影,參演過哪些電視劇,唱過哪些歌,同時又是哪些公司的投資方。同時我們還需要建立一系列關聯表來記錄一部電影中哪些人是主角,哪些人是配角,哪個人是導演,哪些人是特效等。可以看到,我們需要大量的關聯表來記錄這一系列複雜的關系。在更多實體引入之後,我們将需要越來越多的關聯表,進而使得基于關系型資料庫的解決方案繁瑣易錯。

  這一切的症結主要在于關系型資料庫是以為實體模組化這一基礎理念設計的。該設計理念并沒有提供對這些實體間關系的直接支援。在需要描述這些實體之間的關系時,我們常常需要建立一個關聯表以記錄這些資料之間的關聯關系,而且這些關聯表常常不用來記錄除外鍵之外的其它資料。也就是說,這些關聯表也僅僅是通過關系型資料庫所已有的功能來模拟實體之間的關系。這種模拟導緻了兩個非常糟糕的結果:資料庫需要通過關聯表間接地維護實體間的關系,導緻資料庫的執行效能低下;同時關聯表的數量急劇上升。

  這種執行效能到底低下到什麼程度呢?就以建立人和電影之間的投資關系為例。一個使用關聯表的設計常常如下所示:

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

  如果現在我們想要通過該關系找到一部電影的所有投資人,關系型資料庫常常會執行哪些操作呢?首先,在關聯表中執行一個Table Scan操作(假設沒有得到索引支援),以找到所有film域的值與目标電影id相比對的記錄。接下來,通過這些記錄中的person域所記錄的Person的主鍵值來從Person表中找到相應的記錄。如果記錄較少,那麼這步就會使用Clustered Index Seek操作(假設是使用該運算符)。整個操作的時間複雜度将變為O(nlogn):

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

  可以看到,通過關聯表組織的關系在運作時的性能并不是很好。如果我們所需要操作的資料集包含了非常多的關系,而且主要是在對這些關系進行操作,那麼可以想象到關系資料庫的性能将變得有多差。

  除了性能之外,關聯表數量的管理也是一個非常讓人頭疼的問題。剛剛我們僅僅是舉了一個具有四個實體的例子:人,電影,電視劇,影視公司。現實生活中的例子可不是這麼簡單。在一些場景下,我們常常需要對更多的實體進行模組化,進而完整地描述某一領域内的關聯關系。這種關聯關系所涵蓋的可能包含影視公司的控股關系,各控股公司之間複雜的持股關系以及各公司之間的借貸款情況及擔保關系等,更可能是人之間的關系,人與各個品牌之間的代言關系,各個品牌與所屬公司之間的關系等。

  可以看到,在需要描述大量關系時,傳統的關系型資料庫已經不堪重負。它所能承擔的是較多實體但是實體間關系略顯簡單的情況。而對于這種實體間關系非常複雜,常常需要在關系之中記錄資料,而且大部分對資料的操作都與關系有關的情況,原生支援了關系的圖形資料庫才是正确的選擇。它不僅僅可以為我們帶來運作性能的提升,更可以大大提高系統開發效率,減少維護成本。

  在一個圖形資料庫中,資料庫的最主要組成主要有兩種,結點集和連接配接結點的關系。結點集就是圖中一系列結點的集合,比較接近于關系資料庫中所最常使用的表。而關系則是圖形資料庫所特有的組成。是以對于一個習慣于使用關系型資料庫開發的人而言,如何正确地了解關系則是正确使用圖形資料庫的關鍵。

  注:這裡的結點集是我自己的翻譯。在Neo4J官方文檔中,其被稱為label。原文為:A label is a named graph construct that is used to group nodes into sets; all nodes labeled with the same label belongs to the same set。我個人覺得生硬地取名為标簽反而容易讓别人混淆,是以選取了“group nodes into sets”的意譯,也好讓label和node,即結點集和結點之間的關系能夠更好地對應。

  但是不用擔心,在了解了圖形資料庫對資料進行抽象的方式之後,您就會覺得這些資料抽象方式實際上和關系型資料庫還是非常接近的。簡單地說,每個結點仍具有标示自己所屬實體類型的标簽,也既是其所屬的結點集,并記錄一系列描述該結點特性的屬性。除此之外,我們還可以通過關系來連接配接各個結點。是以各個結點集的抽象實際上與關系型資料庫中的各個表的抽象還是有些類似的:

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

  但是在表示關系的時候,關系型資料庫和圖形資料庫就有很大的不同了:

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

  從上圖中可以看到,在需要表示多對多關系時,我們常常需要建立一個關聯表來記錄不同實體的多對多關系,而且這些關聯表常常不用來記錄資訊。如果兩個實體之間擁有多種關系,那麼我們就需要在它們之間建立多個關聯表。而在一個圖形資料庫中,我們隻需要标明兩者之間存在着不同的關系,例如用DirectBy關系指向電影的導演,或用ActBy關系來指定參與電影拍攝的各個演員。同時在ActBy關系中,我們更可以通過關系中的屬性來表示其是否是該電影的主演。而且從上面所展示的關系的名稱上可以看出,關系是有向的。如果希望在兩個結點集間建立雙向關系,我們就需要為每個方向定義一個關系。

  也就是說,相對于關系資料庫中的各種關聯表,圖形資料庫中的關系可以通過關系能夠包含屬性這一功能來提供更為豐富的關系展現方式。是以相較于關系型資料庫,圖形資料庫的使用者在對事物進行抽象時将擁有一個額外的武器,那就是豐富的關系:

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

  是以在為圖形資料庫定義資料展現時,我們應該以一種更為自然的方式來對這些需要展現的事物進行抽象:首先為這些事物定義其所對應的結點集,并定義該結點集所具有的各個屬性。接下來辨識出它們之間的關系并建立這些關系的相應抽象。

  是以一個圖形資料庫中所承載的資料最終将有類似于下圖所示的結構:

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

設計一個優質的圖

  在了解了圖形資料庫的基礎知識之後,我們就要開始嘗試使用圖形資料庫了。首先我們要搞清楚一個問題,那就是如何為我們的圖形資料庫定義一個設計良好的圖?實際上這并不困難,您隻需要了解圖資料庫設計時所使用的一系列要點即可。

  首先就是,厘清圖中結點集,結點以及關系之間的互相聯系。在以往的基于關系型資料庫的設計中,我們常常會使用一個表來抽象一類事物。如對于人這個概念,我們常常會抽象出一個表,并在表中添加表示兩個人的記錄,Alice和Bob:

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

  而在圖資料庫中,這裡對應着兩個概念:結點集和結點。在通常情況下,圖形資料庫中的資料展示并不使用結點集,而是獨立的結點:

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

  而如果需要在圖中添加對書籍的支援,那麼這些書籍将仍然被表示為一個結點:

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

  也就是說,雖然在一個圖資料庫中常常擁有結點集的概念,但是它已經不再作為圖資料庫的最重要抽象方式了。甚至從某些圖形資料庫已經允許軟體開發人員使用Schemaless結點這一點上來看,它們已經将結點集的概念弱化了。反過來,我們思考的角度就應該是結點個體,以及這些個體之間所存在的一系列關系。

  那麼我們是不是可以随便定義各個結點所具有的資料呢?不是的。這裡最為常用的一個準則就是:Schemaless這種靈活度能為你帶來好處。例如相較于強類型語言,弱類型語言可以為軟體開發人員帶來更大的開發靈活度,但是其維護性和嚴謹性常常不如強類型語言。同樣地,在使用Schemaless結點時也要兼顧靈活性和維護性。

  這樣我們就可以在結點中添加多種多樣的關系,而不用像在關系型資料庫中那樣需要擔心是否需要通過更改資料庫的Schema來記錄一些外鍵。這進而允許軟體開發人員在各結點間添加多種多樣的關系:

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

  是以在一個圖形資料庫中,結點集這個概念已經不是最重要的那一類概念了。例如在某些圖形資料庫中,各個結點的ID并不是按照結點集來組織的,而是根據結點的建立順序來賦予的。在調試時您可能會發現,某個結點集内的第一個結點的ID是1,第二個結點的ID就是3了。而具有2這個ID的結點則處于另一個結點集中。

  那麼我們應該如何為業務邏輯定義一個合适的圖呢?簡單地說,單一事物應該被抽象為一個結點,而同一類型的結點被記錄在同一個結點集中。結點集内各結點所包含的資料可能有一些不同,如一個人可能有不同的職責并由此通過不同的關系和其它結點關聯。例如一個人既可能是演員,可能是導演,也可能是演員兼導演。在關系型資料庫中,我們可能需要為演員和導演建立不同的表。而在圖形資料庫中,這三種類型的人都是人這個結點集内的資料,而不同的僅僅是它們通過不同的關系連接配接到不同的結點上了而已。也就是說,在圖形資料庫中,結點集并不會像關系型資料庫中的表一樣粒度那麼小。

  一旦抽象出了各個結點集,我們就需要找出這些結點之間所可能擁有的關系。這些關系不僅僅是跨結點集的。有時候,這些關系是同一結點集内的結點之間的關系,甚至是同一結點指向自身的關系:

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

  這些關系通常都具有一個起點和終點。也就是說,圖形資料庫中的關系常常是有向的。如果希望在兩個結點之間建立一個互相關系,如Alice和Bob彼此相識,我們就需要在他們之間建立兩個KNOW_ABOUT關系。其中一個關系由Alice指向Bob,而另一個關系則由Bob指向Alice:

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

  需要注意的一點就是,雖然說圖形資料庫中的關系是單向的,但是在一些圖形資料庫的實作中,如Neo4J,我們不僅僅可以查找到從某個結點所發出的關系,也可以找到指向某個結點的各個關系。也就是說,雖然圖中的關系是單向的,但是關系在起點和終點都可以被查找到。

在項目中使用Neo4J

  OK,在大概了解了圖形資料庫的一些基礎知識之後,我們就将以Neo4J為例講解如何使用一個圖形資料庫了。Neo4J是Neo Technology所提供的開源圖形資料庫。其按照上面所介紹的結點/關系模型組織資料,并擁有以下一系列特性:

  • 對事務的支援。Neo4J強制要求每個對資料的更改都需要在一個事務之内完成,以保證資料的一緻性。
  • 強大的圖形搜尋能力。Neo4J允許使用者通過Cypher語言來操作資料庫。該語言是特意為操作圖形資料庫設計的,是以其可以非常高效地操作圖形資料庫。同時Neo4J也提供了面向目前市場一系列流行語言的用戶端,以供使用這些語言的開發人員能夠快速地對Neo4J進行操作。除此之外,一些項目,如Spring Data Neo4J,也提供了一系列非常簡單明了的資料操作方式,使得使用者上手變得更為容易。
  • 具有一定的橫向擴充能力。由于圖中的一個結點常常具有和其它結點相關聯的關系,是以像一系列Sharding解決方案那樣對圖進行切割常常并不現實。是以Neo4J目前所提供的橫向擴充方案主要是通過Read Replica進行的讀寫分割。反過來,由于單個Neo4J執行個體可以存儲幾十億個結點及關系,是以對于一般的企業級應用,這種橫向擴充能力已經足夠了。

  好,現在我們就來看一個通過Cypher來建立并操作圖形資料庫的例子(來自http://neo4j.com/developer/guide-data-modeling/):

1 // 建立Sally這個Person類型的結點,該結點的name屬性為Sally,age屬性為32
 2 CREATE (sally:Person { name: 'Sally', age: 32 })
 3 
 4 // 建立John結點
 5 CREATE (john:Person { name: 'John', age: 27 })
 6 
 7 // 建立Graph Databases一書所對應的結點
 8 CREATE (gdb:Book { title: 'Graph Databases',
 9                    authors: ['Ian Robinson', 'Jim Webber'] })
10 
11 // 在Sally和John之間建立朋友關系,這裡的since值應該是timestamp。請自行回憶各位的日期是如何在關系資料庫中記錄的~~~
12 CREATE (sally)-[:FRIEND_OF { since: 1357718400 }]->(john)
13 
14 // 在Sally和Graph Databases一書之間建立已讀關系
15 CREATE (sally)-[:HAS_READ { rating: 4, on: 1360396800 }]->(gdb)
16 
17 // 在John和Graph Databases一書之間建立已讀關系
18 CREATE (john)-[:HAS_READ { rating: 5, on: 1359878400 }]->(gdb)      

  該段語句建立了三個結點:Person結點Sally和John,以及Book結點gdb。同時還指定了它們之間的關系:

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

注:原圖來自http://neo4j.com/developer/guide-data-modeling/

想節省時間花在有用的地方,但是為了完整性不得不寫

  這裡有一點需要注意的地方,那就是關系是單向的。如果希望建立一個雙向的關系,就像上面Sally和John互為朋友關系那樣,我們按理來說應該需要重複執行建立關系的過程。由于我沒有試過最新版本的Neo4J(因為它最近有一個破壞了後向相容性的更改,我們暫時沒有辦法更新Neo4J,也就沒有辦法确認上面的代碼是不是少建立了一次FRIEND_OF),是以請讀者注意。如果有誰實驗了,請将結果添加到Comment中,感激不盡。

  有了資料,我們就可以對資料進行操作了。雖然Cypher和SQL操作的是不同的資料結構,但是他們的文法結構還是非常相似的。例如下面的語句就用來獲得Sally和John是什麼時候成為朋友的(來自http://neo4j.com/developer/guide-data-modeling/):

1 MATCH (sally:Person { name: 'Sally' })
2 MATCH (john:Person { name: 'John' })
3 MATCH (sally)-[r:FRIEND_OF]-(john)
4 RETURN r.since as friends_since      

  而且還有一些更複雜的文法。如下面的操作就用來判斷Sally和John誰先讀了《Graph Databases》一書:

1 MATCH (people:Person)
2 WHERE people.name = 'John' OR people.name = 'Sally'
3 MATCH (people)-[r:HAS_READ]->(gdb:Book { title: 'Graph Databases' })
4 RETURN people.name as first_reader
5 ORDER BY r.on
6 LIMIT 1      

  當然,誰都不願意寫SQL,否則Hibernate也發展不起來。一個目前較為流行的解決方案就是Spring Data Neo4J。通過定義一系列Java類并在其上使用一系列标記,我們就能在系統中使用Neo4J了。現在我們就以3.4.4版本的Spring Data Neo4J為例講解如何對其進行使用。

  首先,我們需要為将要存入到Neo4J中的資料定義相應的資料類型(來自于http://projects.spring.io/spring-data-neo4j/):

1 // 通過NodeEntity标記來建立一個需要被存入到Neo4J的資料類型
 2 @NodeEntity
 3 public class Movie {
 4    // 通過GraphId标記來指定作為ID的域。如果是建立一個結點,那麼我們需要将該域置空(null)。不知道4.0.0是否還有這個限制
 5    @GraphId Long id;
 6 
 7    // 建立針對該域的索引
 8    @Indexed(type = FULLTEXT, indexName = "search")
 9    String title;
10 
11    // 對Person類的直接引用。在儲存時,其将會被自動儲存到Person結點集中并保持Movie類對該執行個體的引用
12    Person director;
13 
14    // 通過RelatedTo标記來标示目前集合所引用的各個實體對應于目前Movie執行個體的關系是ACTS_IN。注意這裡标明了方向是INCOMING,也就是說,其方向是從Person指向Movie,也既是Person ACTS_IN Movie。而在Person中,我們同樣可以擁有一個Movie的集合,同樣使用RelatedTo标記使用ACTS_IN關系,而direction為OUTGOING
15    // 另,RelatedTo和RelatedToVia标記按理來說在4.0.0裡已經被丢棄了,但是在官方示例中依然被使用
16    @RelatedTo(type="ACTS_IN", direction = INCOMING)
17    Set<Person> actors;
18 
19    @RelatedToVia(type = "RATED")
20    Iterable<Rating> ratings;
21 
22    // 使用自定義Query讀取資料
23    @Query("start movie=node({self}) match 
24              movie-->genre<--similar return similar")
25    Iterable<Movie> similarMovies;
26 }      

  接下來,您就可以建立一個用來對剛剛所定義的類型進行CRUD操作的Repository了:

1 // 從GraphRepository接口直接得到對Movie類進行CRUD的功能
 2 interface MovieRepository extends GraphRepository<Movie> {
 3    // 通過Cypher語句來執行特定的操作
 4    @Query("start movie={0} match m<-[rating:RATED]-user
 5              return rating")
 6    Iterable<Rating> getRatings(Movie movie);
 7 
 8    // 和Spring Data JPA一樣,可以通過特定的規則組合函數名來添加篩選條件
 9    Iterable<Person> findByActorsMoviesActorName(name)
10 }      

  最後我們需要在Spring的配置檔案中指定這些組成所在的位置:

1 <neo4j:config storeDirectory="target/graph.db" base-package="com.example.neo4j.entity"/>
2 <neo4j:repositories base-package="com.example.neo4j.repository" />      

Neo4J叢集

  OK,在了解了如何使用Neo4J之後,下一步要考慮的就是如何通過搭建一個Neo4J叢集來提供一個具有高可用性,高吞吐量的解決方案了。首先您要知道的是,和其它NoSQL資料庫所提供的近乎無限的橫向擴充能力相比,Neo4J叢集實際上是有一定限制的。為了能更好地了解這些限制,就讓我們首先看一看Neo4J叢集的架構以及它到底是如何工作的:

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

  上圖展示了一個由三個Neo4J結點所組成的Master-Slave叢集。通常情況下,每個Neo4J叢集都包含一個Master和多個Slave。該叢集中的每個Neo4J執行個體都包含了圖中的所有資料。這樣任何一個Neo4J執行個體的失效都不會導緻資料的丢失。叢集中的Master主要負責資料的寫入,接下來Slave則會将Master中的資料更改同步到自身。如果一個寫入請求到達了Slave,那麼該Slave也将會就該請求與Master通信。此時該寫入請求将首先被Master執行,再異步地将資料更新到各個Slave中。是以在上圖中,您可以看到表示資料寫入方式的紅線有從Master到Slave,也有從Slave到Master,但是并沒有從Slave到Slave。而所有這一切都是通過Transaction Propagation組成來協調完成的。

  有些讀者可能已經注意到了:Neo4J叢集中資料的寫入是通過Master來完成的,那是不是Master會變成系統的寫入瓶頸呢?答案是幾乎不會。首先是圖資料修改的複雜性導緻其并不會像棧,數組等資料類型那樣容易被修改。在修改一個圖的時候,我們不但需要修改圖結點本身,還要維護各個關系,本身就是一個比較複雜的過程,對使用者而言也是較難了解的。是以對圖所進行的操作也常常是讀比寫多很多。同時Neo4J内部還有一個寫隊列,可以用來暫時緩存向Neo4J執行個體的寫入操作,進而使得Neo4J能夠處理突然到來的大量寫入操作。而在最壞的情況就是Neo4J叢集需要面對持續的大量的寫入操作。在這種情況下,我們就需要考慮Neo4J叢集的縱向擴充了,因為此時橫向擴充無益于解決這個問題。

  反過來,由于資料的讀取可以通過叢集中的任意一個Neo4J執行個體來完成,是以Neo4J叢集的讀吞吐量可以在理論上做到随叢集中Neo4J執行個體的個數線性增長。例如如果一個擁有5個結點的Neo4J叢集可以每秒響應500個讀請求,那麼再添加一個結點就可以将其擴容為每秒響應600個讀請求。

  但在請求量非常巨大而且通路的資料分布非常随機的情況下,另一個問題就可能發生了,那就是Cache-Miss。Neo4J内部使用一個緩存記錄最近所通路的資料。這些緩存資料會儲存在記憶體中以便快速地響應資料讀取請求。但是在請求量非常巨大而且所通路資料分布随機的情況下,Cache-Miss将會持續地發生,使得每次對資料的讀取都要經過磁盤查找來完成,進而大大地降低了Neo4J執行個體的運作效率。而Neo4J所提供的解決方案被稱為Cache-based Sharding。簡單地說,就是使用同一個Neo4J執行個體來響應一個使用者所發送的所有需求。其背後的原理也非常簡單,那就是同一個使用者在一段時間内所通路的資料常常是類似的。是以将這個使用者的一系列資料請求發送到同一個Neo4J伺服器執行個體上可以很大程度上降低發生Cache-Miss的機率。

  Neo4J資料伺服器中的另一個組成Cluster Management則用來負責同步叢集中各個執行個體的狀态,并監控其它Neo4J結點的加入和離開。同時其還負責維護上司選舉結果的一緻性。如果Neo4J叢集中失效的結點個數超過了叢集中結點個數的一半,那麼該叢集将隻接受讀取操作,直到有效結點重新超過叢集結點數量的一半。

  在啟動時,一個Neo4J資料庫執行個體将首先嘗試着加入由配置檔案所标明的叢集。如果該叢集存在,那麼它将作為一個Slave加入。否則該叢集将被建立,并且其将被作為該叢集的Master。

  如果Neo4J叢集中的一個Neo4J執行個體失效了,那麼其它Neo4J執行個體會在短時間内探測到該情況并将其标示為失效,直到其重新恢複到正常狀态并将資料同步到最新。這其中有一個特殊情況,那就是Master失效的情況。在該情況下,Neo4J叢集将會通過内置的Leader選舉功能選舉出新的Master。

  在Cluster Management組成的幫助下,我們還可以建立一個Global Cluster。其擁有一個Master Cluster以及多個Slave Cluster。該叢集組建方式允許Master Cluster和Slave Cluster處于不同區域的服務叢集中。這樣就可以允許服務的使用者通路距離自己最近的服務。和Neo4J叢集中的Master及Slave執行個體的關系類似,資料的寫入通常都是在Master Cluster中進行,而Slave Cluster将隻負責提供資料讀取服務。

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

提高Neo4J的性能

  相信您在上面對Neo4J叢集的講解中已經看出,Neo4J叢集實際上還是有一些限制的。這些限制将可能導緻Neo4J叢集在總的系統容量,如存儲結點的數目或寫吞吐量等衆多方面存在着一定的瓶頸。在《服務的擴充性》一文中我們曾經介紹過,通過縱向擴充,我們同樣可以提高服務的整體性能。除了通過為Neo4J提供具有更高容量的硬體之外,更有效地使用Neo4J也是縱向擴充的一個重要方法。

  和SQL Server等資料庫所提供的Execution Plan類似,Neo4J也提供了Execution Plan。在執行一個請求時,Neo4J将會把這個請求拆解為一系列較小的操作符(Operator)。每個操作符都将執行一部分工作,并彼此互相協作完成對請求的響應。與SQL Server的Execution Plan類似,Neo4J的Execution Plan同樣擁有Scan,Seek,Merge,Filter等多種類型的操作。我們可以通過EXPLAIN或PROFILE指令得到一個請求将被如何執行的樹形表示。通過檢視這些樹形表示,軟體開發人員能夠了解一個請求在Neo4J中是如何運作的:

開源軟體:NoSql資料庫 - 圖資料庫 Neo4j轉載自原文位址:http://www.cnblogs.com/loveis715/p/5277051.html

  以下是目前版本的Neo4J所支援的所有操作符:http://neo4j.com/docs/stable/execution-plans.html。

  有通過Execution Plan調優經驗的讀者可能第一眼就看到了一個操作符:Node Index Seek。它的名字直接透露了Neo4J中的另一個調優利器:索引。我們知道,隻要查找出的結果集中記錄的資料并不是很多,那麼SQL Server中的Clusted Index Seek常常是具有最優效率的操作。是以在Neo4J中,我們同樣需要盡量合理地使用索引,進而使得Neo4J所生成的Execution Plan能使用基于索引的一系列操作符。讓我們回憶一下之前展示給大家的按照Spring Data Neo4J方式抽象出的Movie類:

1 @NodeEntity
2 public class Movie {
3    @GraphId Long id;
4 
5    @Indexed(type = FULLTEXT, indexName = "search")
6    String title;
7 
8    ……
9 }      

  上面的代碼展示了我們應該如何通過@Indexed标記來建立一個索引。如果您是直接使用Cypher來操作Neo4J的,那麼您可以通過以下語句來建立一個索引:

1 CREATE INDEX ON :Movie(title)      

  而這裡有一個和SQL Server略有不一緻的地方,那就是對@GraphId标記的了解。在SQL Server中,Primary Key實際上是與索引沒有任何關聯的,隻是在預設情況下,其常常會被自動地添加一個索引。而在Neo4J中,由@GraphId标記所修飾的域則更像是Neo4J的内部實作。Neo4J通過該域所記錄的值執行對結點的通路,而不是在其上自動地添加了一個索引。

  還有一個可能非常容易影響Neo4J性能的可能,那就是嘗試使用Neo4J記錄不适合它記錄的資料。在本文的一開始我們就已經介紹了Neo4J所适合的領域,那就是記錄圖形資料,及結點集和結點之間的關系。而對于其它一些類型的資料,如使用者的使用者名/密碼對,那就不是圖形資料庫所擅長的領域了。在這些情況下,我們應該選取合适的資料庫來記錄這些資料。在一個大型系統中,多種不同類型的資料庫互相協作是經常有的事情,是以沒有必要非要将一些本來應該由其它類型的資料庫所記錄的資料硬生生地記錄在Neo4J中。

和其它資料庫合作

  在上面我們剛剛提到了不應該由Neo4J記錄不适合它記錄的資料,以保證Neo4J不被不合理的使用方式拉低其執行效率。那麼這些資料應該記錄在哪裡呢?答案非常簡單:适合記錄這些資料的其它類型的資料庫。

  可能你覺得我這句話是廢話。其實我也這麼覺得。而我想在這裡介紹的是,如何完成Neo4J和其它資料庫之間的內建,進而使它們協同工作,向使用者提供完整的服務。對于某些系統,我們可以允許這些資料庫之間擁有一定程度的不一緻;而對于另外一些系統,我們則需要時刻保證資料的一緻性。

  Neo4J所提出支援的技術方案主要有三種:Event-based Synchronization,Periodic Synchronization以及Periodic Full Export/Import of Data。Event-based Synchronization實際上就是通過同時向基于Neo4J的背景和基于其它資料庫的背景發送相同的消息,并由這些背景完成對資料的寫入。Periodic Synchronization則是定時地将一個資料庫中對資料的更改同步到另一個資料庫中。而Periodic Full Export/Import of Data則是通過将一個資料庫中的所有資料導入到另外一個資料庫中的方式來完成的。

  這三種解決方案都是用來處理Neo4J所記錄的資料與其它資料庫相同的情況。而更為常見的情況則是,Neo4J記錄實體關系比較複雜的圖,其它資料庫則用來記錄具有其它類型表現形式的資料。Neo4J和這些資料庫之間的資料隻有一部分交集,而每個資料庫都擁有自己所特有的資料。針對這種情況的處理方法則常常是多步送出。例如在一個交友網站中,使用者可以在頁面上完成自身賬戶的設定,如使用者名,密碼等,并可以在下一步添加好友界面中添加一系列好友以及有關于該好友的注釋。那麼在該系統中,使用者自身的賬戶設定就可能記錄在關系型資料庫中,而有關好友的相關資訊則記錄在圖形資料庫中。如果将這兩步中的所有資訊作為一個請求發送到背景,那麼就可能出現在某個資料庫上成功儲存而在另一個資料庫上儲存失敗的情況。為了避免這種情況,我們就需要将填充這兩部分資料的資訊分為兩個頁面,而在每個頁面下部提供一個”儲存并進行下一步”的按鈕。這樣如果第一步設定賬戶的步驟無法正常儲存,那麼使用者就沒有辦法進行下一步添加朋友的操作。而在添加朋友這步中,如果圖形資料庫無法正常儲存,那麼我們将可以明确地告訴使用者添加朋友失敗,進而允許使用者重試。

  其實很多時候,跨不同資料庫儲存資料的問題都可以通過調整設計的方式來解決,況且這些資料庫所記錄的資料常常具有非常不同的資料結構。是以就使用者來說,分成多步送出常常是一個非常自然的使用方式。

轉載于:https://www.cnblogs.com/lexiaofei/p/7081624.html