Cypher筆記(二)

Cypher筆記(二)

1.模式

索引和約束時圖的模式,Cypher引入了數據定義語言(Data Definition Language,DDL)來操作模式。

a.索引

索引是爲了提高數據檢索效率而引入的冗餘信息,代價是需要維護額外的數據使得寫入時更慢。Cypher允許所有節點的某個屬性上有特定的標籤。索引一旦創建,它將自己管理並當圖發生變化時自動更新。多因創建並生效後,Neo4j將自動開始使用索引。

CREATE INDEX ON :Person(name) // 在擁有Person標籤的所有節點的name屬性上創建了索引
DROP INDEX ON :Person(name) // 刪除擁有Person標籤的所有節點的name屬性上的索引
MATCH (person:Person { name: 'Andres' }) RETURN person
// 查詢中不需要指出使用哪個索引,Cypher自己會決定,這裏如果存在Person(name)索引,Cypher會自動使用

b.約束

Neo4j通過約束保證數據完整性。約束可用於節點或者關係,可以創建節點屬性的唯一性約束,也可以創建節點和關係的屬性存在約束。
可以使用屬性的唯一性約束確保擁有特定標籤的所有節點或者擁有特定類型的所有關係的屬性是存在的,所有的試圖創建新的沒有該屬性的節點或關係,以及試圖刪除強制屬性的查詢都將失敗。
可以使用屬性的存在性約束確保擁有特定標籤的所有節點或者擁有特定類型的所有關係的屬性是存在的,所有試圖創建新的沒有改屬性的節點或關係,以及試圖刪除強制屬性的查詢都將失敗。只有Neo4j企業版有屬性存在性約束這個高級功能。

  • 可以對某個給定標籤添加多個約束,唯一性約束和存在性約束可以同時添加到同一個屬性上
  • 添加約束是一個需要花一段時間的原子操作,在所有已存在的數據被掃描完之前,這個約束不會生效
  • 若已存在某個節點或關係沒有某個屬性,再對這個屬性創建存在性約束會失敗
CREATE CONSTRAINT ON (book:Book) ASSERT book.isbn IS UNIQUE // 創建唯一性約束
DROP CONSTRAINT ON (book:Book) ASSERT book.isbn IS UNIQUE // 刪除唯一性約束
CREATE CONSTRAINT ON (book:Book) ASSERT exists(book.isbn) // 創建節點屬性存在性約束
DROP CONSTRAINT ON (book:Book) ASSERT exists(book.isbn) // 刪除節點屬性存在性約束
CREATE CONSTRAINT ON ()-[like:LIKED]-() ASSERT exists(like.day) // 創建關係屬性存在性約束

c.統計

當執行一個Cypher查詢時,它將先編譯爲一個執行計劃。爲了給查詢提供一個高效的計劃,Neo4j需要數據庫的信息

  • 擁有特定標籤的節點的數量
  • 每個索引的可選擇性
  • 按類型分的關係的數量
  • 以擁有指定標籤的節點開始或者結束的關係,按類型分各自的數量

配置這些統計信息的更新
* dbms.index_sampling.background_enabled 控制當需要更新時索引是否自動重新採樣
* dbms.index_sampling.update_percentage 控制多大比例的索引被更新後才觸發新的採樣
* cypher.statistics_divergence_threshhold 控制一個執行計劃被認爲過時要重新生成前,允許多少統計信息發生變化。任何統計信息的相對變化超過臨界值,原有執行計劃將被丟棄並創建一個新的計劃,0.0=有變化就更新,1.0=永遠不更新

Cypher根據可用索引構建執行計劃,這些索引決策一直有效直到數據模式發生變化。所以增加和刪除索引都會導致緩存的執行計劃被刷新

手動索引採樣
* cypher-shell ‘CALL db.resampleIndex(“:Person(name)”);’
* cypher-shell ‘CALL db.resampleOutdatedIndexes();’

2.查詢調優

查詢性能優化的總目標是確保只從圖中檢索必要的數據,不必要的數據應該儘可能早地被過濾掉,以減少後期處理的數據量。

查詢計劃器

查詢計劃器有用於產生查詢計劃的規則。它會考慮所有可用的索引,但不使用統計信息去指導查詢編譯。計劃器使用統計服務爲所有可選的查詢賦予一個成本,然後選擇耗費最少的那個。默認情況下,Neo4j 3.1對所有查詢使用成本計劃器。可以通過使用 cypher.planner 配置來強制使用制定的計劃器。

  • 查看查詢計劃:在查詢語句中加入EXPLAIN
  • 查看哪個運算符佔了大部分工作:在查詢語句中加入PROFILE。此時會跟蹤傳遞了多少行數據給每個運算符,以及每個運算符和存儲曾交互了多少以獲取必要的數據。注意PROFILE會佔用更多的資源,建議只用於做性能分析

USING

USING語句用於爲一個查詢構建執行計劃時影響計劃器的決定。通過USING來強制Neo4j使用一個特定的開始點,這個被稱爲計劃器提示
* 索引提示用於告知計劃器無論在什麼情況下都應使用指定的索引作爲開始點。使用在MATCH後添加USING INDEX variable:Label(property)來補充索引提示。可以使用多個索引提示,但多個開始點會在後面的查詢計劃中潛在地需要額外的連接
* 掃描提示:在MATCH後面使用USING SCAN variable:Label,將強制Cypher不使用本應使用的索引,而採用標籤掃描
* 連接提示:強制在特定的點進行連接。使用USING JOIN ON variable

3.執行計劃

Neo4j把執行一個查詢的任務分解爲一些稱爲運算符的小塊,這些以模式形式連接在一起的運算符稱爲一個執行計劃。每個運算符用如下統計信息來註解:

  • Rows:Row運算符產生的行數,只有帶有profile的查詢纔有
  • EstimatedRows:使用基於成本的編譯器,可以看到由運算符產生的預估的行數。編譯器使用這個估值來選擇合適的執行計劃
  • DbHits:每個運算符都會向Neo4j存儲引擎請求像檢索或者更新數據這樣的工作,一次數據庫命中是存儲引擎工作的一個抽象單位

開始點運算符

這些運算符用於找到圖的開始點

  • 全節點掃描(AllNodesScan):從節點庫讀取所有節點
  • 通過id搜索有向關係(DirectedRelationshipByIdSeekPipe):從關係庫通過id來讀取一個或多個關係將返回關係和兩端的節點
  • 通過id尋找節點(NodeByIdSeek)
  • 通過標籤掃描檢索節點(NodeByLabelScan)
  • 通過索引檢索節點(NodeIndexSeek)
  • 通過索引範圍尋找節點(NodeIndexSeekByRange):使用索引檢索節點,節點的屬性值滿足給定的字符串前綴,用於STARTS WITH和比較符號
  • 通過索引包含掃描檢索節點(NodeIndexContainsScan):一個節點的包含掃描將遍歷存儲在索引中的所有值,搜索實體中是否包含指定的字符串
  • 通過索引掃描檢索節點(NodeIndexScan):遍歷存儲在索引中的所有值,找到擁有特定標籤和屬性的所有節點(如 exists(n.prop))
  • 通過id尋找無方向關係(UndirectedRelationshipByIdSeek)

Expand元算符

  • Expand All(Expand(All)):給定一個開始節點,expand-all將根據關係中的模式沿開始節點或者結束節點展開,能處理變長模式的關係
  • Expand Into(Expand(Into)):當開始節點和結束節點都已經找到時,expand-into用於找到兩個節點之間連接的所有關係
  • 可選Expand All(OptionalExpand(All)):從一個給定節點開始遍歷關係,確保在返回結果之前斷言得到處理,針對OPTOINAL MATCH

組合運算符

256 用於將其他運算符拼接在一起

  • Apply:Apply以嵌套循環的方式工作。Apply運算符左端返回的每一行作爲右端運算符的輸入,然後Apply將產生組合的結果
  • SemiApply:測試一個模式斷言的存在性。SemiApply從它的子運算符中獲取一行,並將其作爲右端的葉節點運算符的輸入。如果右端運算符至少產生一行結果,左端的這一行由SemiApply運算符產生。這使得SemiApply成爲一個過濾運算符,可以大量運用在查詢的模式斷言中
  • AntiSemiApply:測試一個模式斷言的存在性。和SemiApply相反,它進行反向過濾
  • LetSemiApply:測試模式斷言的存在性。當一個查詢包含多個模式斷言時,LetSemiApply將用於處理它們中的地一個。它會記錄斷言的評估結果,但會留下過濾器到另外一個運算符
  • LetAntiSemiApply:當一個查詢包含多個模式斷言時,LetAntiSemiApply將用於處理它們中的第一個。它會記錄斷言的評估結果,但會留下過濾器到另外一個運算符
  • SelectOrSemiApply:測試一個模式斷言的存在性並評估一個斷言。這個運算符允許將一般的斷言與檢查存在性的斷言放在一起。首先評估普通表達式,僅當它返回false時模式斷言纔會執行
  • SelectOrAntiSemiApply:測試一個模式斷言的存在性並評估一個斷言
  • ConditionalApply:檢查一個變量是否不爲null,如果是那麼執行右邊的部分
  • AntiConditionalApply:檢查一個變量是否爲null,如果是那麼執行右邊的部分
  • AssertSameNode:用於確保沒有違背唯一性約束
  • NodeHashJoin:使用哈系表,NodeHashJoin將來自左端和右端的輸入連接起來
  • 三元(Triadic):用於解決三元查詢,如查找我朋友的朋友中那些還不是我朋友的人。它把所有的朋友放入一個集合,然後再檢查他們是否已經與我相連

行運算符

將其他運算符產生的行轉換爲一個新的行集合

  • Eager:確保在繼續之前將那些會影響後續操作的運算在整個數據集上被完全地執行
  • Distinct:移除輸入流中重複的行
  • Eager聚合:即時加載潛在的結果並存入哈系map中,使用分組鍵作爲map的鍵
  • 從計數庫獲取節點數量:比通過計數方式的Eager聚合更快,但計數庫只保存了有限範圍的組合,因此Eager聚合對很多複雜的查詢依然很有用
  • 從計數庫獲取關係數量:同上
  • 過濾:過濾來自在子運算符的每一行,僅僅讓斷言成爲true的結果通過
  • Limit:返回輸入的前n行
  • Projection:對於輸入的每一行,projection將評估表達式併產生一行表達式的結果
  • Skip:跳過輸入行的前n行
  • Sort:根據給定的鍵進行排序
  • Top:返回根據給定鍵排序後的前n行
  • Union:將左右兩個計劃的結果連接在一起
  • Unwind:將列表中的值以每行一個元素的形式返回
  • 調用過程:返回以name爲序的所有標籤

更新運算符

  • 約束操作(CreateUniqueConstrain):在一對標籤和屬性上創建一個約束
  • EmptyResult(EmptyResult):即時加載產生所有結果到EmptyResult運算符並丟棄掉
  • 更新圖(UpdateGraph):對圖進行更新操作
  • Merge Into(Merge(Into)):當開始和結果節點都已經找到,Merge Into用於找到這兩個節點之間的所有關係或者創建一個新的關係

最短路徑規劃

用快速算法檢索最短路徑

MATCH (ms:Person { name: 'Martin Sheen' }), (cs: Person { name: 'Charlie Sheen' }), p = shortestPath((ms)-[rels:ACTED_IN*]-(cs))
WHERE ALL (r IN rels WHERE exists(r.role)) RETURN p

這個查詢可以使用快速算法——因爲沒有斷言需要查看所有路徑

需要檢查路徑上額外斷言的最短路徑規劃

  • 考慮使用窮舉搜索:在決定哪條是最短的匹配路徑之前,WHERE語句中的斷言需要應用於最短路徑模式
MATCH (cs:Person { name: 'Charlie Sheen' }), (ms:Person { name: 'Martin Sheen'}), p = shortestPath((cs)-[*]-(ms))
WHERE length(p) > 1 RETURN p // 與上個例子不同,這個查詢在知道哪條是最短路徑之前,需要檢查所有的路徑
  • 禁止使用窮舉搜索算法:使用with語句將使得查詢計劃不使用窮舉搜索算法。有快速算法找到的任何路徑接下來將被過濾掉,這可能會導致沒有結果返回
MATCH (cs:Person { name: 'Charlie Sheen' }), (ms:Person { name: 'Martin Sheen' }), p  = shortestPath((cs)-[*]-(ms))
WITH p WHERE length(p) > 1 RETURN p
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章