圖形資料庫Neo4J簡介

来源:https://www.cnblogs.com/bigmonkey/archive/2018/05/17/9049411.html
-Advertisement-
Play Games

轉載:http://www.cnblogs.com/loveis715/p/5277051.html 最近我在用圖形資料庫來完成對一個初創項目的支持。在使用過程中覺得這種圖形資料庫實際上挺有意思的。因此在這裡給大家做一個簡單的介紹。 NoSQL資料庫相信大家都聽說過。它們常常可以用來處理傳統的關係型 ...


轉載:http://www.cnblogs.com/loveis715/p/5277051.html

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

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

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

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

 

圖形資料庫簡介

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

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

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

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

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

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

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

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

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

  註:這裡的結點集是我自己的翻譯。在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,即結點集和結點之間的關係能夠更好地對應。

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

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

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

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

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

  因此一個圖形資料庫中所承載的數據最終將有類似於下圖所示的結構:

 

設計一個優質的圖

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

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

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

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

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

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

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

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

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

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

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

  需要註意的一點就是,雖然說圖形資料庫中的關係是單向的,但是在一些圖形資料庫的實現中,如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。同時還指定了它們之間的關係:

註:原圖來自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集群的架構以及它到底是如何工作的:

  上圖展示了一個由三個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將只負責提供數據讀取服務。

 

提高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中是如何運行的:

  以下是當前版本的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和這些資料庫之間的數據只有一部分交集,而每個資料庫都擁有自己所特有的數據。針對這種情況的處理方法則常常是多步提交。例如在一個交友網站中,用戶可以在頁面上完成自身賬戶的設置,如用戶名,密碼等,並可以在下一步添加好友界面中添加一系列好友以及有關於該好友的註釋。那麼在該系統中,用戶自身的賬戶設置就可能記錄在關係型資料庫中,而有關好友的相關信息則記錄在圖形資料庫中。如果將這兩步中的所有信息作為一個請求發送到後臺,那麼就可能出現在某個資料庫上成功保存而在另一個資料庫上保存失敗的情況。為了避免這種情況,我們就需要將填充這兩部分資料的信息分為兩個頁面,而在每個頁面下部提供一個”保存併進行下一步”的按鈕。這樣如果第一步設置賬戶的步驟無法正常保存,那麼用戶就沒有辦法進行下一步添加朋友的操作。而在添加朋友這步中,如果圖形資料庫無法正常保存,那麼我們將可以明確地告訴用戶添加朋友失敗,從而允許用戶重試。

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

 


 

轉載:http://www.cnblogs.com/loveis715/p/5277051.html

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 什麼是存儲過程: 存儲過程是一組為了完成特定功能的sql語言集,存儲過程經過編譯後儲存在資料庫服務端中,類似oracle創建自增長SEQUENCE。 存儲過程的功能及優點: 儲存過程只在創建時編譯一次存儲在資料庫服務端中,以後調用無需再編譯,可以重覆使用,比一般的sql語句執行效率快,並且可以設定某 ...
  • 本文鏈接:http://www.cnblogs.com/zhenghongxin/p/9050219.html 使用兩種備份方案 備份方案選擇RDB和AOF同時進行備份,必須打開AOF的持久化機制,除非能接受在故障環境下丟失幾分鐘的數據。 在redis重啟的時候,是優先通過AOF進行數據恢復的,因為 ...
  • MySQL 5.5.32 配置文件優化詳解 ============================== [TOC] 一、配置文件說明 MySQL 5.5.32是Mysql5.5系列中最後一個版本,也是最後一個有配置文件的版本,為什麼這麼說呢,用過5.6的博友都知道,在mysql5.6中已經不提供配 ...
  • 引言 昨天和剛入行就帶我的老領導相約北京酒吧,4年師徒情,7年未見,從老公司境況到老熟人的現狀,到現在的工作,未來的發展。從當下的技術到新技術的展望,聊到資料庫架構,我說我現在還是在做傳統的資料庫架構,而老領導滿心的分散式,好像不是分散式都是比較LOW了,這裡面依然存在著這樣一個問題,什麼是“分散式 ...
  • 如何查看Windows伺服器安裝了那些SQL Server組件呢? 最近就遇到這樣一個需求,需要知道Windows伺服器是否安裝了Replication組件,那麼有幾種方法查看Windows伺服器安裝了哪些SQL Server組件呢?下麵總結一下這方面的方法,希望對遇到這樣問題的人有所幫助! 1:通... ...
  • 存儲過程太多,有時只記得存儲過程裡面的某一點類容,想要找到對應的存儲過程: SELECT obj.Name 存儲過程名, sc.TEXT 存儲過程內容 FROM syscomments sc INNER JOIN sysobjects obj ON sc.Id = obj.ID WHERE sc.T ...
  • Hadoop是一個開源的分散式系統框架 一.集群準備 1. 三台虛擬機,操作系統Centos7,三台主機名分別為k1,k2,k3,NAT模式 2.節點分佈 k1: NameNode DataNode ResourceManager NodeManager k2: SecondNameNode Dat ...
  • 下載並解壓 1. 從官方網站下載最新安裝包 2. 解壓到目標安裝目錄 新建配置文件 1. 在安裝目錄新建my.ini文件 2. 添加如下內容(需修改為自己的配置) 啟動服務 1. 使用命令行模式,跳轉到安裝目錄下的bin目錄 2. 執行安裝命令:mysqld install 3. 執行啟動命令:ne ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...