在上一篇文章中,介紹了在 Neo4j 建立索引和約束,來增加查詢效率或確保資料的完整、一致性,那麼說到效率,我們怎麼知道查詢的效率好壞呢?今天要介紹的是如何讓 Neo4j 查詢最佳化,以及提升效率還有哪些作法。
在 Cypher 語法前面加上 EXPLAIN
,就可以檢視查詢計畫,但 EXPLAIN 後面的敘述不會真的被執行。相對的,所能得到的計畫資訊只是一部分。
EXPLAIN MATCH (c) WHERE c.companyName = 'Wartian Herkku'
RETURN c
在 Cypher 語法前面加上 PROFILE
,就可以更細節的檢視查詢計畫,除了 EXPLAIN 的所有資訊外,還可額外得知記憶體用量與 db hits 等資訊。
PROFILE 後面的 Cypher 敘述會真的執行,比 EXPLAIN 佔用更多系統資源,在非常大量的資料庫做初步的效能分析時,建議先採用 EXPLAIN 尋找問題,然後再斟酌使用 PROFILE。
PROFILE MATCH (c) WHERE c.companyName = 'Wartian Herkku'
RETURN c
Cypher version: CYPHER 4.1, planner: COST, runtime: PIPELINED. 13747 total db hits
從執行結果看到,上述的查詢共發生了 13747 次 db hits,這是 Neo4j 資料庫的抽象存取單位,另外我刻意忽略了後面還有花了多少時間執行,因為那只是參考,通常第一次都跑比較久,第二次的執行時間會大幅降低。
上面兩張圖,都表示著 Cypher 的執行計畫(Execution Plan)
,而圖中每個階段就是一個運算子(Operator)
,而每個運算子都會接收來自上一個運算子的結果,執行運算後再將資料送到下一個運算子,直到取得最後查詢結果。
以上圖為例,執行的步驟如下:
對查詢計畫有概念後,接著我們可以開始逐步優化它
第一個問題便是:我們明明知道 companyName 就是 Customer 的欄位;或是反過來說,我們就是要查詢客戶資料,那為何不限制只查詢 Customer 呢?
就好比關聯式資料庫,我們幾乎不會一次查詢所有的資料表。
把上述語法改進如下:
PROFILE MATCH (c:Customer) WHERE c.companyName = 'Wartian Herkku'
RETURN c
Cypher version: CYPHER 4.1, planner: COST, runtime: PIPELINED. 387 total db hits
從原本 12710 db hits 大幅縮小到 387 db hits !!!
接著看執行計畫也出現新的運算子
NodeByLabelScan
這是掃瞄 Customer 所有節點,全部的 Customer 節點就是 91 個。後面的運算子與步驟就都類似了。
而在實務的應用上,如果 companyName 被查詢的頻率很高,可考慮加上索引。我們來看看加上索引後的執行狀況
CREATE INDEX ON :Customer(companyName)
Cypher version: CYPHER 4.1, planner: COST, runtime: PIPELINED. 2 total db hits
這次竟然只花費了兩次 db hits !!!
並且用了 NodeIndexSeek 運算子,也就是有用到索引來提升效率。
關於執行計畫與運算子,可以參考官網有更詳細的資訊 https://neo4j.com/docs/cypher-manual/current/execution-plans
下次再為大家介紹更進階的查詢優化方式