最近我在用圖形數據庫來完成對一個初創項目的支持。在使用過程當中以爲這種圖形數據庫實際上挺有意思的。所以在這裏給你們作一個簡單的介紹。html
NoSQL數據庫相信你們都據說過。它們經常能夠用來處理傳統的關係型數據庫所難以解決的一系列問題。一般狀況下,這些NoSQL數據庫分爲Graph,Document,Column Family以及Key-Value Store等四種。這四種類型的數據庫分別使用了不一樣的數據結構來記錄數據。所以它們所適用的場景也不盡相同。node
其中最爲特別的即是圖形數據庫了。能夠說,它和其它的一系列NoSQL數據庫很是不一樣:豐富的關係表示,完整的事務支持,卻沒有一個純正的橫向擴展解決方案。spring
在本文中,咱們就將對業界很是流行的圖形數據庫Neo4J進行簡單的介紹。sql
圖形數據庫簡介數據庫
相信您和我同樣,在使用關係型數據庫時經常會遇到一系列很是複雜的設計問題。例如一部電影中的各個演員經常有主角配角之分,還要有導演,特效等人員的參與。一般狀況下這些人員經常都被抽象爲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所提供的開源圖形數據庫。其按照上面所介紹的結點/關係模型組織數據,並擁有如下一系列特性:
好,如今咱們就來看一個經過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
商業轉載請事先與我聯繫:silverfox715@sina.com
公衆號必定幫忙別標成原創,由於協調起來太麻煩了。。。