【问题标题】:deduplicating ArangoDB document collection去重 ArangoDB 文档集合
【发布时间】:2016-06-08 13:36:22
【问题描述】:

我确信有一种简单快捷的方法可以做到这一点,但它让我无法理解。我有一个包含一些重复记录的大型数据集,我想摆脱重复记录。 (重复项由一个属性唯一标识,但文档的其余部分也应该相同)。

我试图创建一个新的集合,它只有几种不同的方式具有唯一值,但它们都很慢。例如:

FOR doc IN Documents
    COLLECT docId = doc.myId, doc2 = doc
    INSERT doc2 IN Documents2

FOR doc IN Documents
    LET existing = (FOR doc2 IN Documents2
        FILTER doc.myId == doc2.myId
        RETURN doc2)
    UPDATE existing WITH doc IN Documents2

或者(这给了我一个“违反唯一约束”的错误)

FOR doc IN Documents
    UPSERT {myId: doc.myId}}]}
    INSERT doc
    UPDATE doc IN Documents2

【问题讨论】:

  • 如果你说你有一个大数据集并且你当前的查询速度很慢,那在数字上意味着什么? (数据大小、文档数量、查询执行时间、ArangoDB 版本、系统规格,如 RAM 量和大容量存储设备类型)
  • 220 万个文档,3-4gb 集合大小。我有 16gb RAM,我在 SSD 上运行 Arango(现在是 3.0),但我的交换在 HDD 上。查询花费了超过 30 分钟(可能更多,但我还没等看到)。
  • 您可以尝试建议的查询(或至少 COLLECT ... KEEP 变体)并发表有关您系统性能的评论吗?谢谢!

标签: arangodb aql


【解决方案1】:

TL;DR

至少在我的台式机上(Windows 10,Intel 6700K 4x4.0GHz ,32GB RAM,Evo 850 SSD)。

某些查询需要正确的索引,否则它们将永远存在。索引需要一些内存,但与查询执行期间对记录进行分组所需的内存相比,它可以忽略不计。如果内存不足,性能会受到影响,因为操作系统需要在内存和大容量存储之间交换数据。这对于旋转磁盘来说尤其是一个问题,而对于快速闪存存储设备来说则不然。

准备

我生成了 220 万条记录,其中包含 5-20 个随机属性和每个属性 160 个乱码字符。此外,每条记录都有一个属性myid。 187k 条记录具有唯一 id,60k myids 存在两次,70k 存在 3 次。集合大小报告为 4.83GB:

// 1..2000000: 300s
// 1..130000: 20s
// 1..70000: 10s
FOR i IN 1..2000000
    LET randomAttributes = MERGE(
        FOR j IN 1..FLOOR(RAND() * 15) + 5
            RETURN { [CONCAT("attr", j)]: RANDOM_TOKEN(160) }
    )
    INSERT MERGE(randomAttributes, {myid: i}) INTO test1

启动 ArangoDB 前的内存消耗为 3.4GB,启动后为 4.0GB,加载 test1 源集合后约为 8.8GB。

基线

test1 读取并将所有文档 (2.2m) 插入test2 在我的系统上花费了 20 秒,内存峰值约为 17.6GB:

FOR doc IN test1
    INSERT doc INTO test2

在不写入的情况下按myid 分组大约需要。对我来说是 9s,查询期间内存峰值为 9GB:

LET result = (
    FOR doc IN test1
        COLLECT myid = doc.myid
        RETURN 1
)
RETURN LENGTH(result)

分组失败

我在一个只有 3 条记录和一个重复的 myid 的数据集上尝试了您的 COLLECT docId = doc.myId, doc2 = doc 方法。它表明查询实际上并没有分组/删除重复项。因此,我试图寻找替代查询。

用 INTO 分组

要将重复的myids 组合在一起但保留访问完整文档的可能性,可以使用COLLECT ... INTO。我只是选择了每个组的第一个文档来删除多余的myids。查询用了大约 40 秒将具有唯一 myid 属性的 2m 条记录写入 test2。我没有准确测量内存消耗,但我看到了跨越 14GB 到 21GB 的不同内存峰值。也许截断测试集合并重新运行查询会增加所需的内存,因为一些过时的条目会以某种方式阻碍(压缩/密钥生成)?

FOR doc IN test1
    COLLECT myid = doc.myid INTO groups
    INSERT groups[0].doc INTO test2

用子查询分组

以下查询显示内存消耗更稳定,峰值为 13.4GB:

FOR doc IN test1
    COLLECT myid = doc.myid
    LET doc2 = (
        FOR doc3 IN test1
            FILTER doc3.myid == myid
            LIMIT 1
            RETURN doc3
    )
    INSERT doc2[0] INTO test2

但是请注意,它需要在 test1 中的 myid 上使用哈希索引来实现约 38 秒的查询执行时间。否则子查询将导致数百万次集合扫描并需要很长时间。

用 INTO 和 KEEP 分组

我们可以只将_id 分配给一个变量并将KEEP 分配给一个变量,而不是将整个文档存储在一个组中,这样我们就可以使用DOCUMENT() 查找文档正文:

FOR doc IN test1
    LET d = doc._id
    COLLECT myid = doc.myid INTO groups KEEP d
    INSERT DOCUMENT(groups[0].d) INTO test2

内存使用:加载源集合后为 8.1GB,查询期间峰值为 13.5GB。 2m 条记录只用了 30 秒

使用 INTO 和投影进行分组

出于好奇,我还尝试了投影,而不是 KEEP:

FOR doc IN test1
    COLLECT myid = doc.myid INTO groups = doc._id
    INSERT DOCUMENT(groups[0]) INTO test2

加载test1 后,RAM 为 8.3GB,峰值为 17.8GB(实际上在查询执行期间出现了两次严重的峰值,均超过 17GB)。完成 200 万条记录需要 35 秒。

更新插入

我用 UPSERT 尝试了一些东西,但看到了一些奇怪的结果。事实证明,这是 ArangoDB 的 upsert 实施中的一个疏忽。 v3.0.2 contains a fix 我现在得到正确的结果:

FOR doc IN test1
    UPSERT {myid: doc.myid}
    INSERT doc
    UPDATE {} IN test2

test2 中使用myid 上的(唯一)哈希索引处理需要 40 秒,RAM 峰值约为 13.2GB。

就地删除重复项

我首先将所有文档从test1 复制到test2(220 万条记录),然后我尝试将REMOVE 仅复制test2 中的重复项:

FOR doc IN test2
    COLLECT myid = doc.myid INTO keys = doc._key
    LET allButFirst = SLICE(keys, 1) // or SHIFT(keys)
    FOR k IN allButFirst
        REMOVE k IN test2

内存为 8.2GB(仅加载了 test2),在查询期间增加到 13.5GB。删除重复项大约需要 16 秒 (200k)。

验证

以下查询将myid 组合在一起,并汇总每个 id 出现的频率。对目标集合test2运行,结果应该是{"1": 2000000},否则还是有重复的。我仔细检查了上面的查询结果并检查了所有内容。

FOR doc IN test2
    COLLECT myid = doc.myid WITH COUNT INTO count
    COLLECT c = count WITH COUNT INTO cc
    RETURN {[c]: cc}

结论

ArangoDB v3.0 的性能似乎是合理的,但如果没有足够的 RAM 可用,性能可能会下降。不同的查询大致在同一时间内完成,但显示出不同的 RAM 使用特性。对于某些查询,索引是必要的以避免高计算复杂度(这里:完整的集合扫描;在最坏的情况下读取 2,200,000,000,000 次?)。

您能否在您的数据上尝试我提出的解决方案并检查您机器上的性能?

【讨论】:

  • 性能在我的 16gb 机器上似乎相似:基线为 4.8 到 9.6,COLLECT...INTO 上为 5.4 到 13.1,COLLECT 上为 5.1 到 10.0,带有子查询,COLLECT...INTO...KEEP 上为 5.1 到 13.7。带投影的COLLECT...INTO 使用最多,5.1 到 14.2gb,我认为它开始使用一些交换。他们花了 30-50 秒。
猜你喜欢
  • 1970-01-01
  • 2020-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多