如何改进 arangodb 中的更新查询
How to improve Update query in arangodb
我有一个 collection,其中包含超过 1500 万份文档。在这 1500 万份文档中,我每小时更新 20k 条记录。但是更新查询需要很长时间才能完成(大约 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 索引。
Update
我无法在查询中手动使用 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
我假设选择部分(不是更新部分)将是此查询的瓶颈。
查询似乎有问题,因为对于匹配第一个过滤器 (h.dt == DATE_ISO8601(...)
) 的每个文档,将对 instArr
数组中的 20,000 个值进行迭代。如果 instArr
个值是唯一的,则只有其中一个值匹配。此外,内部循环不会使用索引,因为索引选择已经在外部循环中发生。
与其循环遍历 instArr
中的所有值,不如将伴随的 ==
比较转换为 IN
比较。如果 instArr
是一个实例名称数组,那已经可以工作了,但它似乎是一个实例对象数组(至少由属性 inst
和 count
组成)。为了在 IN
比较中使用实例名称,最好有一个专用的实例名称数组,以及 count
和 dt
的翻译 table值。
以下是使用 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
所以值不会改变。
我有一个 collection,其中包含超过 1500 万份文档。在这 1500 万份文档中,我每小时更新 20k 条记录。但是更新查询需要很长时间才能完成(大约 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 索引。
Update
我无法在查询中手动使用 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
我假设选择部分(不是更新部分)将是此查询的瓶颈。
查询似乎有问题,因为对于匹配第一个过滤器 (h.dt == DATE_ISO8601(...)
) 的每个文档,将对 instArr
数组中的 20,000 个值进行迭代。如果 instArr
个值是唯一的,则只有其中一个值匹配。此外,内部循环不会使用索引,因为索引选择已经在外部循环中发生。
与其循环遍历 instArr
中的所有值,不如将伴随的 ==
比较转换为 IN
比较。如果 instArr
是一个实例名称数组,那已经可以工作了,但它似乎是一个实例对象数组(至少由属性 inst
和 count
组成)。为了在 IN
比较中使用实例名称,最好有一个专用的实例名称数组,以及 count
和 dt
的翻译 table值。
以下是使用 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
所以值不会改变。