重复数据消除ArangoDB文档集合

重复数据消除ArangoDB文档集合,arangodb,aql,Arangodb,Aql,我相信有一个简单而快速的方法可以做到这一点,但我还是逃避了。我有一个很大的数据集,其中有一些重复的记录,我想消除重复的记录。(副本由一个属性唯一标识,但文档的其余部分也应相同) 我试图创建一个新的集合,它只有几个不同的方法具有唯一的值,但它们都非常缓慢。例如: FOR doc IN Documents COLLECT docId = doc.myId, doc2 = doc INSERT doc2 IN Documents2 或 FOR doc IN Documents

我相信有一个简单而快速的方法可以做到这一点,但我还是逃避了。我有一个很大的数据集,其中有一些重复的记录,我想消除重复的记录。(副本由一个属性唯一标识,但文档的其余部分也应相同)

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

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
或者(这会导致“违反唯一约束”错误)

TL;博士 至少在我的台式机(Windows 10、Intel 6700K 4x4.0GHz、32GB RAM、Evo 850 SSD)上,消除重复记录并将其写入另一个集合不会花费那么长的时间(不到60秒)

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

准备工作 我生成了220万条记录,其中包含5-20个随机属性和每个属性160个字符的胡言乱语。此外,每个记录都有一个属性
myid
。187k条记录具有唯一的id,60k
myid
s存在两次,70k存在三次。收集大小报告为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之后的内存消耗为3.4GB,加载
test1
源代码集合之后的内存消耗约为8.8GB

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

FOR doc IN test1
    INSERT doc INTO test2
myid
分组而不写大约需要9秒,查询期间内存峰值为9GB:

LET result = (
    FOR doc IN test1
        COLLECT myid = doc.myid
        RETURN 1
)
RETURN LENGTH(result)
分组失败 我在一个只有3条记录和一条重复的
myId
数据集上尝试了您的
COLLECT docId=doc.myId,doc2=doc
方法。这表明查询实际上并没有分组/删除重复项。因此,我尝试寻找其他查询

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

使用子查询分组 以下查询显示了更稳定的内存消耗,峰值为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秒的查询执行时间。否则,子查询将导致数以百万计的集合扫描并需要花费时间

分组并保留 我们可以将
\u id
分配给一个变量并
保留它,这样我们就可以使用
document()
查找文档正文,而不是将整个文档存储在一个组中:

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

用入和投影分组 出于好奇,我也尝试了一个投影,而不是保留:

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,我现在得到了正确的结果:

FOR doc IN test1
    UPSERT {myid: doc.myid}
    INSERT doc
    UPDATE {} IN test2
test2
中使用
myid
上的(唯一)散列索引进行处理需要40秒,RAM峰值约为13.2GB

就地删除重复项 我首先将所有文档从
test1
复制到
test2
(2.2m记录),然后我尝试
删除
仅删除
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。删除重复项(200k)大约需要16秒

验证 以下查询将
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使用特征。对于某些查询,索引是避免高计算复杂性所必需的(这里是:完整集合扫描;最坏情况下是2200000000000次读取?)


您能否在您的数据上尝试我提供的解决方案,并检查您的计算机的性能?

如果您说您的数据集很大,并且当前的查询速度很慢,那么这在数字上意味着什么?(数据大小、文档数量、查询执行时间、ArangoDB版本、系统规格(如RAM数量和大容量存储设备类型)220万个文档,3-4gb收集大小。我有16gb的RAM,我在SSD上运行Arango(现在是3.0),但我的交换是在HDD上。查询耗时超过30分钟(可能要长得多,但我并没有等着看)
FOR doc IN test1
    UPSERT {myid: doc.myid}
    INSERT doc
    UPDATE {} IN 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
FOR doc IN test2
    COLLECT myid = doc.myid WITH COUNT INTO count
    COLLECT c = count WITH COUNT INTO cc
    RETURN {[c]: cc}