有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java MongoDB和多个upsert

我对MunGDB比较新,但是我们认为在遗留服务前面使用它作为某种缓存。在这种情况下,我们偶然发现了一些问题

首先,一些解释

此缓存服务将位于遗留服务和客户端之间。客户端将连接到缓存服务,该服务从遗留服务获取数据。缓存服务每X分钟获取一次数据,并将其保存在MongoDB中。模式非常简单:只是一个有很多键/值的文档。没有嵌套文档之类的。此外,我们将_id设置为遗留服务的唯一id,因此我们也可以控制它

当缓存服务从遗留服务获取数据时,它只会获取一个增量(自上次获取后仅发生更改)。因此,如果自上次以来有5个“对象”发生了变化,则只得到这5个“对象”(但得到的是完整的对象,而不是对象的增量)。如果有任何新的“对象”被添加到遗留服务中,这些当然也在增量中

我们的“问题”

在我看来,这听上去像是一次突袭。如果有新对象,请插入它们。如果现有对象发生更改,请更新它们。然而,MongoDB似乎并不特别喜欢多重优势。仅仅插入就给了我一个关于重复键的错误,这是完全可以理解的,因为已经存在具有相同_id的文档。更新函数可以接受upsert参数,但不能接受新对象的列表。在我看来,一个单一的查询是不可能的。不过,我可能完全忽略了这里的一些东西

可能的解决方案

有很多不同的解决方案,尤其是我想到的两个:

  • 执行两个查询:首先,计算一个包含所有_id的列表(记住,我们从遗留服务中获得了这些)。然后,将它们与_id列表一起使用$in函数删除,并立即插入新文档。实际上,这将用新数据更新我们的收集。它也很容易实现。可能出现的一个问题是,客户机在删除和插入之间请求数据,因此错误地得到一个空结果。这是一个交易破坏者,绝对不可能发生
  • 对每个更改的对象执行一次向上插入。也很容易实现,不应该给出与其他解决方案相同的问题。不过,这还有其他(可能是想象中的)问题。它能在短时间内处理多少次升级?它能很容易地处理每分钟5000个加料吗?这些不是大文档,只有大约20个键/值,没有子文档。这个数字是凭空得出的,很难预测实际数字。在我看来,这种方法感觉是错误的。我不明白为什么每个新文档都需要运行一个查询

无论是关于两个提议的解决方案还是任何其他解决方案,我们都将不胜感激。顺便说一句,技术并不是真正可以讨论的,所以请不要建议使用其他类型的数据库或语言。我们选择我们所选择的还有其他强有力的原因:)


共 (4) 个答案

  1. # 1 楼答案

    或者,如果您的钥匙是复合钥匙,您可以使用:

    public static BulkWriteResult insertAll(MongoCollection<Document> coll, List<Document> docs, String[] keyTags, boolean upsert) {
        if(docs.isEmpty())
            return null;
        List<UpdateOneModel<Document>> requests = new ArrayList<>(docs.size());
        UpdateOptions opt = new UpdateOptions().upsert(upsert);
        for (Document doc : docs ) {
            BasicDBObject filter = new BasicDBObject();
            for (String keyTag : keyTags) {
                filter.append(keyTag, doc.get(keyTag));
            }
            BasicDBObject action = new BasicDBObject("$set", doc);
            requests.add(new UpdateOneModel<Document>(filter, action, opt));
        }
        return coll.bulkWrite(requests);
    }
    
  2. # 2 楼答案

    我将分享我的经验

    在我上一份工作中,我们遇到了类似的情况。最后,我们对每个文档/对象进行了一次查询/写入。我们使用Mule ESB将数据从遗留系统传输到Mongo,每次写入都是一次升级

    表演不错,但不太好。我们可以在几分钟内将数千份文件输入Mongo。这些文件相当丰富,所以这可能是我们不得不限制对Mongo的写入的部分原因

    在我们批量加载数据之后,“实时”性能从来都不是问题

    您建议的第一个选项听起来太复杂,可能会让Mongo处于未知状态,以防操作在更新过程中死亡。upsert选项为我们节省了很多时间,因为我们可以反复重放插入内容,从而确保安全

  3. # 3 楼答案

    要详细介绍ryan1234的答案:

    MongoDB的2.6版本将能够发送批量更新。目前,您需要为每个文档提交单独的请求

    正如ryan1234所说,如果您不知道来自旧版提供商的信息,那么对每个文档进行upsert是更新所有现有文档并添加新文档的唯一安全方法。一个MongoDB进程可以轻松处理mid teir硬件上每秒数千次的更新。如果没有达到这样的性能水平,那么可能是客户机和MongDB服务器之间的请求延迟。Asynchronous Java Driver可以帮助克服这一限制,它允许多个更新请求以最小的客户端复杂性/线程同时发送到服务器

    罗布

    1:我假设文档没有增长,也没有索引更新,但即使有这些更新,你也应该能够每秒更新1000次

  4. # 4 楼答案

    我知道。它必须深入挖掘正确的方法。 试试这个: /** *将文档中的所有项目插入到集合中。 *@param coll目标集合 *@param记录新的或更新的文档 *@param keyTag文档中密钥的名称 *@param upsert if true在未找到时创建新文档 *@return BulkWriteResult,如果是文档,则返回null。isEmpty() */

        public static BulkWriteResult insertAll(MongoCollection<Document> coll, List<Document> docs, String keyTag, boolean upsert) {
        if(docs.isEmpty())
            return null;
        List<UpdateOneModel<Document>> requests = new ArrayList<>(docs.size());
        UpdateOptions opt = new UpdateOptions().upsert(upsert);
        for (Document doc : docs ) {
            BasicDBObject filter = new BasicDBObject(keyTag, doc.get(keyTag)); 
            BasicDBObject action = new BasicDBObject("$set", doc);
            requests.add(new UpdateOneModel<Document>(filter, action, opt));
        }
        return coll.bulkWrite(requests);
    }