我确信有一种简单快捷的方法可以做到这一点,但它已经逃脱了我。我有一个大型数据集,有一些重复的记录,我想摆脱重复。 (重复项由一个属性唯一标识,但文档的其余部分也应相同)。
我试图创建一个只有几种不同方式的唯一值的新集合,但它们都很慢。例如:
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
答案 0 :(得分:6)
重复删除记录并将其写入另一个集合(少于60秒)并不需要很长时间,至少在我的台式机上(Windows 10,Intel 6700K 4x4.0GHz) ,32GB RAM,Evo 850 SSD)。
某些查询需要正确编制索引,否则它们将永久存在。索引需要一些内存,但与查询执行期间为记录分组所需的内存相比,它可以忽略不计。如果内存不足,性能将受到影响,因为操作系统需要在内存和大容量存储之间交换数据。对于旋转磁盘而言,这尤其是一个问题,而不是快速闪存设备。
我生成了220万条记录,每个属性有5-20个随机属性和160个乱码乱码。此外,每条记录都有一个属性myid
。 187k条记录具有唯一ID,60k myid
存在两次,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之前的内存消耗在启动4.0GB后为3.4GB,在加载test1
源集合后约为8.8GB。
从test1
读取并将所有文档(2.2m)插入test2
在我的系统上耗时20秒,内存峰值为~17.6GB:
FOR doc IN test1
INSERT doc INTO test2
按myid
分组而不写作约。 9s对我来说,在查询期间有9GB RAM峰值:
LET result = (
FOR doc IN test1
COLLECT myid = doc.myid
RETURN 1
)
RETURN LENGTH(result)
我在仅有3条记录和一条重复COLLECT docId = doc.myId, doc2 = doc
的数据集上尝试了myid
方法。它表明查询实际上并不分组/删除重复项。因此,我试图找到其他查询。
要将重复myid
组合在一起但仍保留访问完整文档的可能性,可以使用COLLECT ... INTO
。我只是选择了每个组的第一个文档来删除多余的myid
。查询花了大约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
但请注意,它需要myid
中test1
上的哈希索引才能实现约38秒的查询执行时间。否则,子查询将导致数百万次扫描并花费很长时间。
我们可以只将_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秒!
而不是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)。完成了2米的记录需要35秒才能完成。
我尝试过UPSERT,但看到了一些奇怪的结果。事实证明这是对ArangoDB upsert实现的疏忽。 v3.0.2 contains a fix我现在得到了正确的结果:
FOR doc IN test1
UPSERT {myid: doc.myid}
INSERT doc
UPDATE {} IN test2
在myid
test2
上使用(唯一)哈希索引进行处理需要40秒,RAM峰值约为13.2GB。
我首先将所有文件从test1
复制到test2
(2.2米记录),然后我尝试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。删除重复项(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使用特性。对于某些查询,索引是必要的,以避免高计算复杂性(这里:完整的集合扫描;在最坏的情况下读取2,200,000,000,000?)。
您是否可以针对您的数据尝试我提供的解决方案并检查您的计算机上的性能?