2014-12-10 38 views
1

我遇到了一個問題,我懷疑是由於我無法制作出高效的CYPHER查詢以及普遍缺乏neo4j體驗。* LONG *執行時間 - 共同興趣

背景:

我有一個比較大的數據集,似乎找到雙方時要窒息用戶和他們的第二學位的朋友之間的「喜歡」。

當前統計:

neo4j-sh (?)$ dbinfo -g "Primitive count" 
{ 
    "NumberOfNodeIdsInUse": 9343080, 
    "NumberOfPropertyIdsInUse": 25416540, 
    "NumberOfRelationshipIdsInUse": 47270718, 
    "NumberOfRelationshipTypeIdsInUse": 8 
} 

------ 

Numbers: 
Users: ~ 2 million 
Likes: ~ 7 million 
Users Likes: ~ 22 million 

指標:

neo4j-sh (?)$ schema 
Indexes 
    ON :Employer(origin_id)  ONLINE (for uniqueness constraint) 
    ON :Group(origin_id)   ONLINE (for uniqueness constraint) 
    ON :Like(category)   ONLINE 
    ON :Like(origin_id)   ONLINE (for uniqueness constraint) 
    ON :Location(country_code) ONLINE 
    ON :Location(country)   ONLINE 
    ON :Location(origin_id)  ONLINE (for uniqueness constraint) 
    ON :School(origin_id)   ONLINE (for uniqueness constraint) 
    ON :User(registered)   ONLINE 
    ON :User(relationship_status) ONLINE 
    ON :User(interested_in)  ONLINE 
    ON :User(gender)    ONLINE 
    ON :User(age)     ONLINE 
    ON :User(origin_id)   ONLINE (for uniqueness constraint) 
    ON :User(uid)     ONLINE (for uniqueness constraint) 

Constraints 
    ON (user:User) ASSERT user.uid IS UNIQUE 
    ON (school:School) ASSERT school.origin_id IS UNIQUE 
    ON (user:User) ASSERT user.origin_id IS UNIQUE 
    ON (group:Group) ASSERT group.origin_id IS UNIQUE 
    ON (employer:Employer) ASSERT employer.origin_id IS UNIQUE 
    ON (like:Like) ASSERT like.origin_id IS UNIQUE 
    ON (location:Location) ASSERT location.origin_id IS UNIQUE 

慢查詢: http://pastebin.com/MPZ3aXCs

問題:

第一個查詢在大約12秒內爲該用戶執行,返回909行。還是很慢。

第二個查詢在大約70秒內爲該用戶執行。對我而言,直接的問題是,試圖通過朋友的一位朋友(第33行)的共同利益進行搜索會導致時間的顯着增加。我還注意到,添加這個匹配似乎在配置文件中創建了第二個EAGER「分支」。在此期間,CPU絕對是固定的。

如果我退一步,簡單地匹配兩個用戶之間的共同興趣,查詢在< 50ms內執行。

neo4j-sh (?)$ PROFILE MATCH (u:User {origin_id:2043})-[:LIKES]->(l:Like)<-[:LIKES]-(u2:User {origin_id:1212817}) return l; 
3 rows 

ColumnFilter 
    | 
    +Filter 
    | 
    +TraversalMatcher 

+------------------+------+--------+-------------+--------------------------------------+ 
|   Operator | Rows | DbHits | Identifiers |        Other | 
+------------------+------+--------+-------------+--------------------------------------+ 
|  ColumnFilter | 3 |  0 |    |      keep columns l | 
|   Filter | 3 |  0 |    |  NOT( UNNAMED31 == UNNAMED50) | 
| TraversalMatcher | 3 | 1114 |    | u2, UNNAMED50, u2, UNNAMED31, u2 | 
+------------------+------+--------+-------------+--------------------------------------+ 

Total database accesses: 1114 

我們目前正在尋求擴展此查詢,以匹配現在看起來不可能的用戶第三度朋友。

我還應該注意到,我在一個獨立的AWS c3.xlarge(4 vCPU/8GB RAM)上運行它,除了主機neo4j以外別無其他。服務器配置或多或少是標準的默認設置。如有必要,很樂意提供。

理想情況下,我希望在單個查詢中返回此信息,這是由於之後處理的方式。

任何幫助優化這些查詢將不勝感激。如果我錯過了任何關鍵信息,請告訴我。

編輯:使用的Neo4j 2.1.6

編輯2:

我做了一些更改查詢這似乎降低幾乎一半dbhits的數量。查詢所用的時間現在減少到約16秒。

與配置文件中的新的查詢,請訪問:http://pastebin.com/UyFi89H7

是否有更多的優化,我可以做,除了使用額外的條件篩選下來的朋友的朋友嗎?

+0

您的鏈接的第二個查詢看起來像從第一個非常不同的查詢。 – 2014-12-12 01:46:19

+0

邁克爾我改變了它的效率稍高。它似乎仍然拉回相同的數據。 – rand411 2014-12-15 15:59:55

回答

1

首先感謝一個很好的詳細問題。其次,通過查看Cypher查詢的開始,我可以給你的建議是從一個小的起點開始,例如,首先匹配你的用戶,然後用WITH將它傳遞給下一步。然後檢索他的位置,通過WITH傳遞用戶和位置。

正如你可以在你的第一個查詢的配置文件中看到的,他將從一個遍歷匹配器開始,而不受益於標籤和屬性索引。

首先優化,把你的道路上:

PROFILE 
MATCH (user:User {origin_id:138}) 
WITH user 
MATCH (user)-[r:LIVES_IN]->(userLoc:Location), (user)-[fr:FRIENDS_WITH*2]->(fof:User) 
WHERE 
    user.origin_id <> fof.origin_id 
    AND NOT (user)-[:FRIENDS_WITH]->(fof) 

有了上面的查詢,他就會使用索引來獲取你的用戶,而不是遍歷匹配。

+0

謝謝Christophe。我採取了這種方式,並試圖對查詢的其餘部分應用相同的邏輯 - 結果各不相同。任何更多的意見和指導將非常感激。 最大的挑戰似乎是將匹配的節點傳遞給查詢。移動喜歡搜索查詢產生積極的結果,直到我跟上FOF查詢。 – rand411 2014-12-11 21:57:19

+0

你能提供一些測試數據嗎? – 2014-12-12 00:00:57

+0

也在您的查詢中,您查找的fof location節點應該與您的userLoc節點相同? – 2014-12-12 00:06:46

0

你也可以試試:

MATCH (u:User {origin_id:2043}),(u2:User {origin_id:1212817}) 
MATCH path = allShortestPaths((u1)-[:LIKES*..2]-(u2)) 
RETURN nodes(path)[1] as like 

嘗試儘可能早地降低基數,以最小的可能, 而不是爲每個FOF多次匹配,儘量聚集到FOF的一個實例第一和匹配然後

PROFILE 
MATCH (user:User {origin_id:138})-[:LIVES_IN]->(userLoc:Location)-[:IN_COUNTRY]->(country) 
MATCH (user)-[fr:FRIENDS_WITH]->(friend:User)-[fofr:FRIENDS_WITH]->(fof:User) 
WHERE (fof.dob_age <= 35 AND fof.dob_age >= 20) 
WITH user, count(distinct friend) as mutual_friend_count, collect(distinct friend) as mutual_friends, fof, 
    (ABS(user.dob_age - fof.dob_age)) as age_diff, userLoc, country 

WHERE (fof)-[:LIVES_IN]->(fofLoc:Location)-[:IN_COUNTRY]->(country) 

RETURN 
    fof.origin_id as fof_origin_id, 
    fof.first_name as fof_first_name, 
    fof.last_name as fof_last_name, 
    fof.dob_age as fof_age, 
    user.dob_age as user_age, 
    userLoc.latitude as user_loc_latitude, 
    userLoc.longitude as user_loc_longitude, 
    fofLoc.name as fof_loc_name, 
    fofLoc.latitude as fof_loc_latitude, 
    fofLoc.longitude as fof_loc_longitude, 
    age_diff as age_diff, 
    mutual_friend_count, mutual_friends