Mongo 3在唯一索引上出现了重复数据 - dropDups

24
在mongoDB的文档中写着: "自3.0版本起,dropDups选项不再可用。" 如果我想要创建唯一索引并删除重复条目,除了降级之外,还有其他能做的吗? 请注意,我每秒会收到约300个插入请求,所以我不能只是删除所有重复项并希望在完成索引之前没有新的重复项出现。

我不明白你的问题。我的理解是,你现在有一些包含重复内容的现有文档,并且你想在包含重复内容的字段上放置一个唯一索引,同时可能会出现新的重复内容? - Markus W Mahlberg
1
是的。我想要摆脱重复项,如果有新的重复项进来就拒绝它们。 - Alonzorz
我也遇到了这个问题,有没有其他方法在MongoDB >= 3.*中去除重复项而不使用dropDups - dr.dimitru
3个回答

18

是的,dropDupes现在已经被弃用,因为无法正确预测哪个文档将被删除。

通常情况下,您有两个选择:

  1. 使用新集合:

    • 创建一个新的集合
    • 在这个新集合上创建唯一索引
    • 运行批处理程序将旧集合中的所有文档复制到新集合中,并确保在此过程中忽略重复键错误。
  2. 手动处理您自己的集合:

    • 确保您的代码不会插入更多重复的文档
    • 在集合中运行批处理程序以删除重复项(如果它们不完全相同,请确保保留好的文档),
    • 然后添加唯一索引。

对于您的特定情况,我建议使用第一种选项,但有一个技巧:

  • 创建一个带有唯一索引的新集合,
  • 更新您的代码,使其现在在两个表格中插入文档,
  • 运行批处理程序将旧集合中的所有文档复制到新集合中(忽略重复键错误),
  • 将新集合重命名为与旧名称匹配,
  • 重新更新您的代码,使其现在只写入“旧”集合。

1
选项1可能是重新创建索引的最佳方式,因为实时系统必须等待索引重建,这可能会减慢其速度。 - Pykler
请确保在处理过程中忽略重复键错误。这该怎么做呢?似乎错误会在处理过程中停止事务。 - Quest
1
使用MongoDB无序批量插入:如果在写操作处理过程中发生错误,MongoDB将继续处理列表中剩余的写操作。 例如:db.persons.insert([{"_id" : "Bob"}, {"_id" : "John"}, {"_id" : "Bob"}, {"_id" : "Marc"}], { ordered : false }) 将插入3个文档并显示一个重复键错误。使用{ordered : true},只有前两个文档会被插入。 更多文档请参见此处 - Maxime Beugnet
1
请注意:MongoDB 中没有事务,它不是 ACID。如果你运行了 10 次插入操作,第五次返回错误时不会回滚。不要将其与文档级锁定混淆,后者是一种只针对单个文档的“迷你”事务。 - Maxime Beugnet
DropDups在MongoDB 2.6版本中已被弃用。https://docs.mongodb.com/v2.6/tutorial/create-a-unique-index/#drop-duplicates - Akansh
关于我之前的评论,现在MongoDB支持跨分片集群(v4.2)的多文档ACID事务:https://www.mongodb.com/transactions - Maxime Beugnet

15

正如@Maxime-Beugnet所强调的那样,您可以创建一个批处理脚本来从集合中删除重复项。我在下面提供了我的方法,如果重复项的数量相对于集合大小较小,则可以实现相对较快的速度。为了演示目的,此脚本将去重以下脚本创建的集合:

db.numbers.drop()

var counter = 0
while (counter<=100000){
  db.numbers.save({"value":counter})
  db.numbers.save({"value":counter})
  if (counter % 2 ==0){
    db.numbers.save({"value":counter})
  }
  counter = counter + 1;
}

通过编写一个聚合查询,返回所有有超过一个重复记录的记录,您可以在此集合中删除重复项。

var cur = db.numbers.aggregate([{ $group: { _id: { value: "$value" }, uniqueIds: { $addToSet: "$_id" }, count: { $sum: 1 } } }, { $match: { count: { $gt: 1 } } }]);

使用游标,您可以迭代重复记录并实现自己的业务逻辑来决定要删除哪些重复项。在下面的示例中,我只保留第一次出现:

while (cur.hasNext()) {
    var doc = cur.next();
    var index = 1;
    while (index < doc.uniqueIds.length) {
        db.numbers.remove(doc.uniqueIds[index]);
        index = index + 1;
    }
}

去重之后,您可以添加唯一索引:

db.numbers.createIndex( {"value":1},{unique:true})

2

pip install mongo_remove_duplicate_indexes

最好的方法是创建一个Python脚本,或使用您喜欢的任何语言,迭代集合,创建一个唯一索引为true的新集合,使用db.collectionname.createIndex({'indexname':1},unique:true),并将先前集合中的文档插入到新集合中。由于您希望不同或重复的键不会插入新集合中,因此可以轻松处理异常。

请查看示例的软件包源代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接