我正在使用Neo4j来代表我们的数据仓库。我们有~100,000个不同类型的节点(~10个),其中一些节点有多个标签。典型节点类型的子集是:
(:User)
(:Tableau:Workbook)
(:Tableau:Dashboard)
此处Tableau
代表数据可视化软件,Workbook
和Dashboard
是不同的Tableau实体。我们使用多个标签而不是单个唯一定义标签的原因是人们可能希望匹配所有(:Tableau)
个节点或所有(:Dashboard)
个节点(我们有多个仪表板源)。
我还使用GraphAware Neo4j UUID库(https://github.com/graphaware/neo4j-uuid)来确保通过uuid
节点属性唯一标识每个节点(不论类型)。
我为每个节点标签创建了一个索引(和唯一性约束)以提高性能,即
CREATE INDEX ON:User(uuid)
CREATE INDEX ON:Tableau(uuid)
CREATE INDEX ON:Workbook(uuid)
CREATE INDEX ON:Dashboard(uuid)
鉴于CREATE INDEX
必须只带一个标签。
在使用此索引结构时使用Cypher匹配节点时,我遇到了一些性能问题。尽管(:Tableau:Dashboard)
<< (:Tableau)
以下查询是次优的
MATCH (n:Tableau:Dashboard) WHERE n.uuid = <UUID>
与之相比
MATCH (n:Tableau) WHERE n.uuid = <UUID>
MATCH (n:Dashboard) WHERE n.uuid = <UUID>
鉴于前者不利用任何索引,尽管后者做了。如果想要仅基于UUID(这是唯一的)全局地找到一个节点,这个问题就更加复杂了,当我们使用Flask API来查找节点时,这通常是这种情况,这转换为以下Cypher逻辑:
MATCH(n) WHERE n.uuid = <UUID>
以下主题建议创建Entity
总体全局节点标签并在其上创建索引(Neo4j: Create index for nodes with same property),
CREATE INDEX ON:Entity(uuid)
所以现在节点标记如下,
(:Entity:User)
(:Entity:Tableau:Workbook)
(:Entity:Tableau:Dashboard)
这是最好的方法吗?另一个解决方案是,如果定义了多个标签,只要选择第一个标签,并确保它被保证索引,但是它不能解决仅基于UUID查找节点的问题。
如果我采用Entity
标签方法,保留所有先前定义的索引仍然有意义,即如果我只搜索节点的一小部分,我是否期望看到显着的性能提升?例如,如果我知道n
是(:User)
节点,我希望看到类似的表演,
MATCH (n:Entity) WHERE n.uuid = <UUID>
MATCH (n:User) WHERE n.uuid = <UUID>
由于最佳Cypher查询可能更抽象,即假设(:Tableau:Workbook)
填充(:Tableau:Dashboard)
然后找到仪表板,因此无法对无索引或多索引进行索引是一种耻辱。工作簿填充一个人会查询,
MATCH (s:Tabeau:Workbook)-[:POPULATES]->(t:Tableau:Dashboard)
WHERE s.uuid = <UUID>
RETURN t
这是相当透明的,但是从性能的角度来看,下面会更加优化,但是由于对用户来说不明显的是什么类型的节点s
,所以不太透明,
MATCH (s:Entity)-[:POPULATES]->(t:Tableau:Dashboard)
WHERE s.uuid = <UUID>
RETURN t
答案 0 :(得分:0)
您正在维护Tableau
和Workbook
以及Tableau
和Dashboard
的重叠索引。为什么不保持Tableau
的索引以消除冗余,并向查询计划器提示USING INDEX
提示,以确保它在您的匹配中使用。就是这样......
MATCH (s:Tableau:Workbook)-[:POPULATES]->(t:Tableau:Dashboard)
USING INDEX s:Tableau(uuid)
WHERE s.uuid = <UUID>
RETURN t