以下示例图表给出了:
CREATE (app1:Application {Id: 1, Name: 'A big application'}),
(db1:Database {Name: 'db1'}),
(db2:Database {Name: 'db2'}),
(di1:DatabaseInstance {Name: 'db1-i1'}),
(di2:DatabaseInstance {Name: 'db1-i2'}),
(di3:DatabaseInstance {Name: 'db2-i1'}),
(di4:DatabaseInstance {Name: 'db2-i2'}),
(s1:Server {Name: 'Server 1'}),
(s2:Server {Name: 'Server 2'}),
(s1)-[:ClusteredWith]->(s2),
(s2)-[:ClusteredWith]->(s1),
(di1)-[:InstalledOn]->(s1),
(di3)-[:InstalledOn]->(s1),
(di2)-[:InstalledOn]->(s2),
(di4)-[:InstalledOn]->(s2),
(di1)-[:Instantiate]->(db1),
(di3)-[:Instantiate]->(db2),
(di2)-[:Instantiate]->(db1),
(di4)-[:Instantiate]->(db2),
(app1)-[:Utilize]->(db1),
(app1)-[:Utilize]->(db2)
这是一个IT库存,我在其中描述了在大型环境中部署的应用程序的关系。实际上,这个应用程序有~500个数据库和~1000个服务器。
我的目标是获取应用程序,数据库或服务器的所有相关资产。
使用Cypher 2我对查询的想法是从应用程序获取所有相关资产:
MATCH (a:Application)
WHERE a.Id = 1
OPTIONAL MATCH (a)-->(d:Database)
OPTIONAL MATCH (a)-[*1..]-(s:Server)
RETURN
a AS Application,
collect(d) AS Databases,
collect(s) AS Servers
使用Neo4jClient:
var result = client.Cypher
.Match("(a:Application)")
.Where((Application a) => a.Id == request.Id)
.OptionalMatch("(a)-->(d:Database)")
.OptionalMatch("(a)-[1..]-(s:Server)")
.Return((a, d, s) => new {
Application = a.As<Anwendung>(),
Databases = d.CollectAs<Database>(),
Servers = s.CollectAs<Server>()
});
我正在使用OPTIONAL MATCH
,因为广告资源图表并不总是处于一致状态。
但是这个查询运行时间太长而且占用了99%的CPU。使用它们,数据库或服务器运行此查询,它可以工作,但查询速度仍然很慢 - &gt; 400-&GT; 900毫秒
我的Datamodel是坏的还是我用错了方法使用Cypher?我怎样才能加快速度?
答案 0 :(得分:4)
当我仅对您提供的示例数据运行该查询时,我得到128个匹配项。您可以通过将return子句更改为
来查看RETURN
a AS Application,
count(d) AS Databases,
count(s) AS Servers
在你的完整数据集上试试吧,我敢打赌你会在棒棒糖中得到一些令人不舒服的比赛。如果查看返回的数据,您还可以看到有大量重复项。即使在您的第一个可选模式中只匹配了两个:Database
节点,您返回的集合为&#34;数据库&#34;包含128项内容。尝试在匹配服务器之前显式清除数据库节点的分辨率,您可以通过在两个可选模式之间引入它来实现此目的
WITH a, collect(d) as Databases
如果您运行此查询并返回计数,则会发现您已将结果减半。您的原始查询将第二个可选模式考虑两次,对于第一个模式中的每个结果一次。当您使用WITH ...
显式解析第一个模式时,第二个模式仅被视为一次。这可能是您在单独运行查询时不那么荒谬的原因。
然而,第二个可选匹配仍然匹配微小数据样本中的64个模式,这可能是导致查询速度缓慢的最终罪魁祸首。这里的问题是你的模式太模糊了。只要最后一个节点是:Server
,模式匹配器就可以在图形中以任何方向和任何深度传播任何类型的关系。有两种方法可以解决这个问题。考虑添加第二个应用程序。此应用程序使用不同的数据库,但托管该数据库实例的其中一个服务器也承载第一个应用程序使用的数据库实例。扩展图形的查询可能看起来像
MATCH (s2:Server {Name: 'Server 2'})
CREATE (app2:Application {Id:2, Name: 'Another application'}),
(db3:Database {Name: 'db3'}),
(di5:DatabaseInstance {Name: 'db3-i1'}),
(di6:DatabaseInstance {Name: 'db3-i2'}),
(s3:Server {name: 'Server 3'}),
(di5)-[:Instantiate]->(db3),
(di5)-[:InstalledOn]->(s3),
(di6)-[:Instantiate]->(db3),
(di6)-[:InstalledOn]->(s2),
(app2)-[:Utilize]->(db3)
你的第二个可选匹配是免费的,以便遍历图表,所以当它确实找到了正确的服务器时,它也可以继续向后行进#39;通过安装在同一服务器上的其他数据库实例。它将遵循
之类的路径(s2:Server {Name: 'Server 2'})<-[:InstalledOn]-(di6:DatabaseInstance {Name: 'db3-i2'})
并且在此路径上可以访问不同的数据库,应用程序不会使用该数据库。一旦到达该数据库,任何与其相关的服务器(实际上,连接到使用该数据库的任何应用程序的任何服务器 - 只是以任何方式以任何方式连接的任何服务器)将被匹配,例如服务器3&#39; :
(di6)-[:Instantiate]->(db3:Database {Name: 'db3'})<-[:Instantiate]-(dbi5:DatabaseInstance {Name: 'db3-i1'})-[:InstalledOn]->(s3:Server {Name: 'Server 3'})
如果您在添加上述应用程序后运行计数查询,则会得到192个结果。仍然只有两条数据库路径,但现在有96条路径到服务器,2x96 = 192。您应该做的是更多地指定第二个模式以排除您不想遵循的路径。由于我只能按照您的样本中的内容进行操作,因此我无法自信地说出更具体的模式应该是什么样的,但您可以从类似
的内容开始OPTIONAL MATCH (a)-[:Utilize]->()<-[:Instantiate]-()-[:InstalledOn]->(server)
我已经从这种模式中排除了标签,这让我最终观察到了......
除了:
...标签非常有用,但它们只是标签。一时间戴上哲学家的帽子,类型会说出什么是什么,标签会说出某人认为某事物是什么。这恰好反映在数据库如何在持久性内存中构建的细节中。关系是打字的,节点不是;关系的类型与它一起存储,标签和属性不与存在它们的节点一起存储。至少对于属性来说这是真的,我对标签并不积极。我理解标签的主要目的是获取模式匹配的起点,这意味着标签主要是对其节点的引用。这可以通过模式动态来证实,其中标签提供索引和约束。您可以将标签视为类似于数学集和关系类型的东西,更像是自然类型(或理想情况下,Aristotelian categoriae )。无论如何,如果我错了,就把我开枪,我说在理论上要求关系类型比在节点的标签或属性上更合理和更便宜。无论..
...出于性能考虑,在解析遍历步骤时,关系的类型在远程节点的标签之前是相关的,并且通常相同类型的关系将导致具有相同标签的节点(这是这种情况)在你的样本中)。实际上,大多数图形建模依赖于意义关系,标签用于为遍历或对象节点映射提供方便的起点。因此,最终的建议通常是在声明模式时强调标签上的关系类型,特别是指定第二个可选模式,如上所述,出于同样的原因,您的第一个可选模式类似于
OPTIONAL MATCH (a)-[:Utilize]->(d)
如果应用程序使用模型中除数据库之外的其他内容,请保留:Database
标签以消除歧义;其他模式也一样。我的观点是:在cypher中声明模式时,不要用标签代替关系类型。
根据您提供的示例,您可以通过将其更改为
来提高查询的性能MATCH (a:Application {Id:1}) // From 2.0RC1 and on you can match with properties
OPTIONAL MATCH (a)-[:Utilize]->(d:Database)
WITH a, collect(d) as Databases
OPTIONAL MATCH (a)-[:Utilize]->()<-[:Instantiate]-()-[:InstalledOn]->(server)
RETURN
a AS Application,
Databases
collect(s) AS Servers