首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >分片后Mongodb插入速度减慢

分片后Mongodb插入速度减慢
EN

Stack Overflow用户
提问于 2017-03-01 02:32:38
回答 2查看 3.2K关注 0票数 9

我有一个Mongodb集群,它包含一个主副本和一个二级副本,作为一个复制集一起运行。但是随着流量的增加,我决定进行切分,以获得更高的写入速度。

我基于这个_id执行了对“教程”列的散列切分,并将数据分割成两个碎片。然后,我执行了一些基准测试,发现在某些情况下,切分的集群比未分割的集群还要慢。

这是测试结果。

  1. 最大吞吐量测试:同时使用十台机器运行"mongoimport“,将数据加载到目标db中,以测试db的最大写入速度。 结果: 切分群集可以插入39500文档/s。 未分割的群集可以插入27400文档/s。
  2. 单实例mongoimport测试:只使用一台机器运行"mongoimport“将数据加载到目标db中。 结果: 切分群集可以插入14285文档/s。 未分割的群集可以插入14085文档/s。
  3. 使用mongodb驱动程序加载单个实例数据:只需使用一个实例就可以通过调用mongodb驱动程序的api将数据加载到目标db中。 结果: 切分群集可以插入4630文档/s。 未分割的群集可以插入17544文档/s。

第一个测试的结果是完全合理的。你将数据库分割成一个2片集群,吞吐量增加了50%左右,一切都很完美,万岁!

第二个测试有点道理。吞吐量大致相同,但瓶颈可能就在数据加载器的旁边,毕竟,我们只是用一个实例加载数据。

但第三个测试真让我烦死了。切分的集群可能比未分割的集群慢得多,这是毫无意义的。另一方面,未切分的数据库具有惊人的速度,甚至比用mongoimport加载数据的速度还要快。

用于加载数据的java代码粘贴在下面。我实在想不出这一点,并预先感谢所有的答案。

代码语言:javascript
复制
public static void insert(String host, int port) throws FileNotFoundException,
        InterruptedException, ExecutionException {
    MongoClient mongoClient = new MongoClient(host, port);
    mongoClient.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
    MongoDatabase database = mongoClient.getDatabase("my-db");
    MongoCollection<Document> collection = database.getCollection("my-collection");
    Scanner scan = new Scanner(new File("my-sample-dataset"));

    // Pre-load the data into the memory, so that the db load test won't be 
    // affected by disk I/O time.
    Queue<List<String>> resource = new LinkedList<>();
    for (int i = 0; i < 100; i++) {
        List<String> strs = new ArrayList<>();
        for (int j = 0; j < 10000; j++)
            strs.add(scan.nextLine());
        resource.add(strs);
    }

    System.out.println("start");
    long startTime = System.currentTimeMillis();
    while (!resource.isEmpty()) {
        List<String> strs = resource.poll();
         List<WriteModel<Document>> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
             list.add(new
             InsertOneModel<Document>(Document.parse(strs.get(i))));
        }
        collection.bulkWrite(list);
    }
    System.out.println("Finished loading. Time taken: " + (System.currentTimeMillis() - startTime) + "ms");
    scan.close();
}
EN

回答 2

Stack Overflow用户

发布于 2017-03-10 10:37:16

以下是可能的罪魁祸首collection.bulkWrite(list);

在大批量书写的情况下,芒果需要将你的批次分解成更小的批次,分给每个碎片。

由于您还没有指定有关批处理中文档的插入顺序的任何内容,所以MongoDB必须按照指定的顺序执行插入的要求。其结果是,连续插入可以批处理当且仅当它们对应于同一个碎片。

mongos保持原始文档顺序,因此只有属于同一碎片的连续插入才能分组。

就像。考虑一下"k“是碎片键的情况。有两个碎片,对应于范围。

代码语言:javascript
复制
[MinKey, 10], (20, MaxKey]

现在假设我们批量插入以下文档:

代码语言:javascript
复制
[{k: 1}, {k: 25}, {k: 2}]

Doc1 -> Shard1,Doc2 -> Shard2,Doc3 -> Shard3

没有两个连续的文档属于同一个碎片,因此在本例中的每个文档之后都需要调用getLastError

在哈希密钥的情况下,文档将更多地随机分布在碎片中。也就是说,属于同一片的文档可能更加分散,因此会产生更多的批,这样的分布越随机,批大小越小,总批数越多,getLastError的发生成本就越高,这实际上意味着性能较差。

FIX:指定"ordered: false"

代码语言:javascript
复制
collection.bulkWrite(list, new BulkWriteOptions().ordered(false));

这告诉数据库,您并不关心严格保留插入发生的顺序。使用"ordered: false",mongos将为每个碎片创建一个批处理,从而避免了额外的getLastError调用。每个批处理操作都可以同时在适当的碎片上执行,而无需等待来自上一批的getLastError响应。

另外,

MongoClient mongoClient = new MongoClient(host, port);

创建基于单个mongodb节点的Mongo实例,并且无法发现副本集或共享集群中的其他节点。

在这种情况下,所有的写请求都被路由到一个节点,该节点负责所有额外的簿记工作,因为分片群集。你应该用的是

代码语言:javascript
复制
MongoClient(final List<ServerAddress> seeds)

当有多个服务器可根据请求类型(读或写)和读首选项(如果是读取请求)选择时,驱动程序将随机选择一个服务器发送请求。这既适用于复制集,也适用于切分群集。 注意:将尽可能多的服务器放到列表中,系统会找到其余的服务器。

票数 7
EN

Stack Overflow用户

发布于 2017-03-09 21:16:42

一般来说,无论何时使用切分解决方案,您都需要考虑以下两种方法之一:

  1. 您的客户端应用程序将具有群集感知能力,因此能够完成路由本身。
  2. 客户端应用程序将与执行路由的中间节点联系。

我的怀疑是Mongo客户端不是“自动”集群感知的,这意味着如果您不指定属于集群的节点,它就不会查找它们。这种感觉因以下几点而得到加强:

您可以通过将ServerAddress列表传递给MongoClient构造函数,从而使用MongoClient驱动程序连接到副本集。例如: MongoClient mongoClient =新MongoClient(Arrays.asList(新ServerAddress("localhost",27017)、新ServerAddress("localhost",27018)、新ServerAddress("localhost",27019));您可以使用相同的构造函数连接到分片集群。MongoClient将自动检测服务器是复制集成员列表还是mongos服务器列表。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42522158

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档