2016-02-29 18 views
1

我开始用下面的查询:为什么我使用count()运行时,我的密码查询需要10倍的时间?

PROFILE 
MATCH Base = (SBase:Snapshot {timestamp:1454983481.304583})-[:contains]->() 
MATCH Prime = (:Snapshot {timestamp:1454983521.642284})-[PContains:contains]->(SPrimePackage) 
WHERE NOT (SBase)-[:contains]->(SPrimePackage) 
RETURN PContains 
LIMIT 10 

我得到 “在119毫秒5834次总DB命中”。该图正确显示了9个节点和8个连接它们的边。然后我运行一个几乎相同的查询,除了我,而不是返回计数(不同的()):

PROFILE 
MATCH Base = (SBase:Snapshot {timestamp:1454983481.304583})-[:contains]->() 
MATCH Prime = (:Snapshot {timestamp:1454983521.642284})-[PContains:contains]->(SPrimePackage) 
WHERE NOT (SBase)-[:contains]->(SPrimePackage) 
RETURN count(distinct(SPrimePackage)) 
LIMIT 10 

这使得“在1771毫秒1382270次总DB命中”。结果是正确的:8.然而,为什么count(distinct())如此慢而且更昂贵?我应该以其他方式做这个吗?

我跑的Neo4j 2.3.1

编辑1

为了确保我比较苹果和苹果,并强调这个问题,这里是一对类似的查询和结果:

MATCH Base = (SBase:Snapshot {timestamp:1454983481.304583})-[:contains]->() 
MATCH Prime = (:Snapshot {timestamp:1454983521.642284})-[PContains:contains]->(SPrimePackage) 
WHERE NOT (SBase)-[:contains]->(SPrimePackage) 
RETURN SPrimePackage 
LIMIT 10 

请注意,它在原始中返回“SPrimePackage”而不是“PContains”。结果是“在740毫秒中总共有5834次数据访问”。

下面是与 “计数()” 完全相同的查询:

MATCH Base = (SBase:Snapshot {timestamp:1454983481.304583})-[:contains]->() 
MATCH Prime = (:Snapshot {timestamp:1454983521.642284})-[PContains:contains]->(SPrimePackage) 
WHERE NOT (SBase)-[:contains]->(SPrimePackage) 
RETURN count(SPrimePackage) 
LIMIT 10 

结果: “在2731毫秒1382270次总DB命中”。注意只有区别在于“count()”。直觉上,我希望“count()”可以添加一个统计步骤,但显然它的作用远不止于此。为什么“count()”会触发所有这些额外的工作?

+0

计数不是问题,不同的是因为在大量命中时耗费时间。我认为你正在寻找的解决方案是基于路径的查询,但我不擅长这一点,我希望有人会给你你需要的。 – Supamiu

+0

嗨@Supamiu感谢您的评论。如果我使用count()而不是count(distinct())运行查询,则会在1454毫秒内得到“1382270总分贝命中数”。然而,那么我的计数就不再是我想要的:3296.有趣的是,3296除以8,这是我正在寻找的答案。 – EdwardTeach

+0

哦,我希望更好的结果没有明显的...尝试添加索引到timestamp属性。 – Supamiu

回答

1

如果你比较2(编辑)查询PROFILE输出[增订]

,你可能会看到,唯一的显著差异的EagerAggregation操作的存在,在COUNT()版本的查询。聚合函数使用EagerAggregation在实际执行聚合函数(在此例中为COUNT())之前收集所有正在聚合的数据。这就需要在不使用聚合功能时不需要的额外工作。

下面的查询仍然使用COUNT()为了得到计数,但大大减少了需要进行汇总数据,从而减少需要在EagerAggregation一步要做的工作量:

PROFILE 
MATCH (SBase:Snapshot { timestamp:1454983481.304583 }) 
USING INDEX SBase:Snapshot(timestamp) 
WHERE (SBase)-[:contains]->() 
MATCH (s:Snapshot { timestamp:1454983521.642284 })-[:contains]->(SPrimePackage) 
USING INDEX s:Snapshot(timestamp) 
WHERE NOT (SBase)-[:contains]->(SPrimePackage) 
RETURN COUNT(DISTINCT SPrimePackage) 
LIMIT 10; 

上面的查询假设你已经在:Snapshot(timestamp)创建索引,大大加快了2个:Snapshot节点搜索:

CREATE INDEX ON :Snapshot(timestamp); 

使用一些简单的数据,我得到的分布是:

+-------------------+----------------+------+---------+--------------------------------------+--------------------------------------+ 
| Operator   | Estimated Rows | Rows | DB Hits | Variables       | Other        | 
+-------------------+----------------+------+---------+--------------------------------------+--------------------------------------+ 
| +ProduceResults |    1 | 1 |  0 | COUNT(DISTINCT SPrimePackage)  | COUNT(DISTINCT SPrimePackage)  | 
| |     +----------------+------+---------+--------------------------------------+--------------------------------------+ 
| +Limit   |    1 | 1 |  0 | COUNT(DISTINCT SPrimePackage)  | Literal(10)       | 
| |     +----------------+------+---------+--------------------------------------+--------------------------------------+ 
| +EagerAggregation |    1 | 1 |  0 | COUNT(DISTINCT SPrimePackage)  |          | 
| |     +----------------+------+---------+--------------------------------------+--------------------------------------+ 
| +AntiSemiApply |    1 | 7 |  0 | anon[180], s -- SBase, SPrimePackage |          | 
| |\    +----------------+------+---------+--------------------------------------+--------------------------------------+ 
| | +Expand(Into) |    1 | 0 |  34 | anon[266] -- SBase, SPrimePackage | (SBase)-[:contains]->(SPrimePackage) | 
| | |    +----------------+------+---------+--------------------------------------+--------------------------------------+ 
| | +Argument  |    4 | 8 |  0 | SBase, SPrimePackage     |          | 
| |     +----------------+------+---------+--------------------------------------+--------------------------------------+ 
| +CartesianProduct |    4 | 8 |  0 | SBase -- anon[180], SPrimePackage, s |          | 
| |\    +----------------+------+---------+--------------------------------------+--------------------------------------+ 
| | +Expand(All) |    4 | 8 |  10 | anon[180], SPrimePackage -- s  | (s)-[:contains]->(SPrimePackage)  | 
| | |    +----------------+------+---------+--------------------------------------+--------------------------------------+ 
| | +NodeIndexSeek |    2 | 2 |  4 | s         | :Snapshot(timestamp)     | 
| |     +----------------+------+---------+--------------------------------------+--------------------------------------+ 
| +SemiApply  |    1 | 2 |  0 | SBase        |          | 
| |\    +----------------+------+---------+--------------------------------------+--------------------------------------+ 
| | +Expand(All) |    4 | 0 |  2 | anon[112], anon[126] -- SBase  | (SBase)-[:contains]->()    | 
| | |    +----------------+------+---------+--------------------------------------+--------------------------------------+ 
| | +Argument  |    2 | 2 |  0 | SBase        |          | 
| |     +----------------+------+---------+--------------------------------------+--------------------------------------+ 
| +NodeIndexSeek |    2 | 2 |  3 | SBase        | :Snapshot(timestamp)     | 
+-------------------+----------------+------+---------+--------------------------------------+--------------------------------------+ 

除了使用索引,上面的查询:

  1. 不打扰找到SBase包含的所有节点,因为我们需要找到一个包含的节点,以便识别匹配的SBase节点。一旦找到单个(SBase)-[:contains]->()匹配,SemiApply操作就会完成,因此第一个MATCH子句将导致每个SBase单行而不是N行。 基于您的问题中的信息,我怀疑N应该是大约8.
  2. 有一个笛卡儿积应该是相当快的,因为产品的“腿”应该具有较低的基数。
+0

这绝对是一个有用的答案。当我将“MATCH Base =(SBase:Snapshot {timestamp:...}) - [:contains] - >()”更改为“MATCH(SBase:Snapshot {timestamp:...})”时,结果为3363总分贝命中1290毫秒。这样更好,所以谢谢。但严格来说,问题是“为什么SPrimePackage和count(SPrimePackage)之间的区别”?如果您可以修改答案以突出回答该问题,我会接受它。 – EdwardTeach

+0

看到我更新的答案。 – cybersam

+0

我有很多要学习!谢谢! – EdwardTeach

相关问题