【问题标题】:How to improve Update query in arangodb如何改进 arangodb 中的更新查询
【发布时间】:2015-12-15 21:05:06
【问题描述】:

我有一个包含超过 1500 万份文档的集合。在这 1500 万份文档中,我每小时更新 2 万条记录。但是更新查询需要很长时间才能完成(大约 30 分钟)。

文档:

{ "inst" : "instance1", "dt": "2015-12-12T00:00:000Z", "count": 10}

我有一个包含 20k 个要更新的实例的数组。

我的查询如下所示:

For h in hourly filter h.dt == DATE_ISO8601(14501160000000) 
   For i in instArr
      filter i.inst == h.inst
      update h with {"inst":i.inst, "dt":i.dt, "count":i.count} in hourly

有没有优化的方法来做到这一点。我在 inst 上有哈希索引,在 dt 上有 skiplist 索引。

更新

我无法在查询中手动使用 20k inst,因此以下是仅 2 inst 的执行计划:

FOR r in hourly FILTER r.dt == DATE_ISO8601(1450116000000) FOR i IN 
 [{"inst":"0e649fa22bcc5200d7c40f3505da153b", "dt":"2015-12-14T18:00:00.000Z"}, {}] FILTER i.inst == 
 r.inst UPDATE r with {"inst":i.inst, "dt": i.dt, "max":i.max, "min":i.min, "sum":i.sum, "avg":i.avg, 
 "samples":i.samples} in hourly OPTIONS { ignoreErrors: true } RETURN NEW.inst

Execution plan:
 Id   NodeType              Est.   Comment
  1   SingletonNode            1   * ROOT
  5   CalculationNode          1     - LET #6 = [ { "inst" : "0e649fa22bcc5200d7c40f3505da153b", "dt" : "2015-12-14T18:00:00.000Z" }, { } ]   /* json expression */   /* const assignment */
 13   IndexRangeNode      103067     - FOR r IN hourly   /* skiplist index scan */
  6   EnumerateListNode   206134       - FOR i IN #6   /* list iteration */
  7   CalculationNode     206134         - LET #8 = i.`inst` == r.`inst`   /* simple expression */   /* collections used: r : hourly */
  8   FilterNode          206134         - FILTER #8
  9   CalculationNode     206134         - LET #10 = { "inst" : i.`inst`, "dt" : i.`dt`, "max" : i.`max`, "min" : i.`min`, "sum" : i.`sum`, "avg" : i.`avg`, "samples" : i.`samples` }   /* simple expression */
 10   UpdateNode          206134         - UPDATE r WITH #10 IN hourly
 11   CalculationNode     206134         - LET #12 = $NEW.`inst`   /* attribute expression */
 12   ReturnNode          206134         - RETURN #12

Indexes used:
 Id   Type       Collection   Unique   Sparse   Selectivity Est.   Fields   Ranges
 13   skiplist   hourly       false    false                 n/a   `dt`     [ `dt` == "2015-12-14T18:00:00.000Z" ]

Optimization rules applied:
 Id   RuleName
  1   move-calculations-up
  2   move-filters-up
  3   move-calculations-up-2
  4   move-filters-up-2
  5   remove-data-modification-out-variables
  6   use-index-range
  7   remove-filter-covered-by-index

Write query options:
 Option                   Value
 ignoreErrors             true
 waitForSync              false
 nullMeansRemove          false
 mergeObjects             true
 ignoreDocumentNotFound   false
 readCompleteInput        true

【问题讨论】:

  • 我猜instArr 是提到的具有 20k 个实例的数组?查询开始时是否知道数组值?或者它是在查询中的某个地方计算的并且没有显示?数组值是否唯一?执行计划是否显示查询使用索引?
  • instArr 在查询开始之前是已知的。它是唯一值数组,长度为 20k。我正在使用 arangodb 2.5.7 并且无法从中升级。我没有尝试执行计划。大多数关于执行计划的文档都是最新版本的。不确定在 2.5.7 中运行哪个命令来执行计划。
  • 可以通过 ArangoShell 中的require("org/arangodb/aql/explainer").explain(queryString); 检索查询的执行计划。如果查询中有绑定参数,可以使用require("org/arangodb/aql/explainer").explain({ query: queryString, bindVars: bindVars });。这对于 2.5 和更新的版本应该是相同的。
  • 我用执行计划编辑了帖子。
  • 答案是否满足您的需求?如果,您介意将其标记为已接受吗?否则,缺少什么?

标签: arangodb


【解决方案1】:

我假设选择部分(而不是更新部分)将成为此查询的瓶颈。

该查询似乎有问题,因为对于与第一个过滤器 (h.dt == DATE_ISO8601(...)) 匹配的每个文档,都会对 instArr 数组中的 20,000 个值进行迭代。如果instArr 值是唯一的,则只有一个值会匹配。此外,内部循环不会使用索引,因为索引选择已经发生在外部循环中。

与其循环遍历instArr 中的所有值,不如将随附的== 比较转换为IN 比较。如果instArr 是一个实例名称数组,这已经可以工作了,但它似乎是一个实例对象数组(至少由属性instcount 组成)。为了在IN 比较中使用实例名称,最好有一个专用的实例名称数组,以及countdt 值的转换表。

以下是使用 JavaScript 生成这些的示例:

var instArr = [ ], trans = { }; 
for (i = 0; i < 20000; ++i) { 
  var instance = "instance" + i;
  var count = Math.floor(Math.random() * 10);
  var dt = (new Date(Date.now() - Math.floor(Math.random() * 10000))).toISOString();
  instArr.push(instance);        
  trans[instance] = [ count, dt ];  
} 

instArr 将如下所示:

[ "instance0", "instance1", "instance2", ... ]

trans:

{ 
  "instance0" : [ 4, "2015-12-16T21:24:45.106Z" ], 
  "instance1" : [ 0, "2015-12-16T21:24:39.881Z" ],
  "instance2" : [ 2, "2015-12-16T21:25:47.915Z" ],
  ...
}

然后可以使用绑定变量(命名类似于上面的变量)将这些数据注入到查询中:

FOR h IN hourly 
  FILTER h.dt == DATE_ISO8601(1450116000000) 
  FILTER h.inst IN @instArr 
  RETURN @trans[h.inst]

请注意,ArangoDB 2.5 尚不支持 @trans[h.inst] 语法。在那个版本中,你需要写:

LET trans = @trans
FOR h IN hourly 
  FILTER h.dt == DATE_ISO8601(1450116000000) 
  FILTER h.inst IN @instArr 
  RETURN trans[h.inst]

此外,2.5 还存在IN 列表较长的问题。 IN-list 性能随IN 列表的长度呈二次方下降。所以在这个版本中,将instArr 的长度限制为最多 2,000 个值是有意义的。这可能需要发出多个带有较小 IN 列表的查询,而不是只发出一个带有大 IN 列表的查询。

更好的选择是使用 ArangoDB 2.6、2.7 或 2.8,它们没有这个问题,因此不需要解决方法。除此之外,您可以在较新的 ArangoDB 版本中使用略短的查询版本。

还要注意,在上述所有示例中,我使用了RETURN ...,而不是原始查询中的UPDATE 语句。这是因为我的所有测试都表明查询的选择部分是主要问题,至少对于我生成的数据而言。 关于UPDATE 原始版本的最后一点说明:用i.inst 更新每个文档的inst 值似乎是多余的,因为i.inst == h.inst 所以值不会改变。

【讨论】:

  • 看起来我在您的问题更新的同时编写并发布了我的答案......但看起来我的假设是正确的:查询使用dt 上的跳过列表索引作为外循环,并且将遍历里面的 20K instArr 值。这绝对应该按照我的回答中的建议更改为 IN 查找。
猜你喜欢
  • 2020-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多