【问题标题】:Mongodb insert speed slower after sharding分片后MongoDB插入速度变慢
【发布时间】:2017-07-20 05:10:14
【问题描述】:

我有一个 Mongodb 集群,其中包含一个主副本和一个辅助副本作为复制集一起运行。但随着流量的增长,我决定执行分片以获得更快的写入速度。

我根据tutorial 对“_id”列执行了散列分片,并将数据分成两个分片。然后我进行了一些基准测试,发现在某些情况下,分片集群甚至比非分片集群还要慢。

这是测试结果。

  1. 最大吞吐量测试:使用十台机器同时运行“mongoimport”将数据加载到目标db,以测试db的最大写入速度。

    结果:

    分片集群可以插入 39500 个文档/秒。

    非分片集群可以插入 27400 个文档/秒。

  2. 单实例mongoimport测试:仅使用一台机器运行“mongoimport”将数据加载到目标数据库中。

    结果:

    分片集群可以插入14285个文档/秒。

    非分片集群可以插入 14085 个文档/秒。

  3. mongodb java驱动的单实例数据加载:调用mongodb java驱动的api,只使用一个实例将数据加载到目标数据库中。

    结果:

    分片集群可以插入 4630 个文档/秒。

    非分片集群可以插入 17544 个文档/秒。

第一次测试的结果非常合理。你将 db 分片成一个 2-shard 集群,吞吐量增加了大约 50%,一切都很完美,万岁!

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

但是第三个测试真的让我很烦。分片集群比非分片集群慢得多是没有意义的。另一方面,未分片的数据库具有惊人的速度,甚至比使用 mongoimport 加载数据还要快。

用于加载数据的 java 代码粘贴在下面。我真的无法弄清楚这一点,并提前感谢所有答案。

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();
}

【问题讨论】:

  • 您是否尝试通过传递所有副本集主机和端口来创建MongoClinet?类似new MongoClient(Arrays.asList( new ServerAddress("localhost", 27017), new ServerAddress("localhost", 27018), new ServerAddress("localhost", 27019)));
  • 您的源数据是否为每条记录指定了_id?
  • 这里还有一些提示:chat.stackoverflow.com/rooms/40058/… 尝试检查主机的统计信息,以及文档是如何在它们之间分片的
  • 你用的是哪个版本的mongo?

标签: java mongodb performance bulkinsert


【解决方案1】:

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

在批量写入的情况下,mongos 需要将您的批次分解成更小的批次,然后进入每个分片。

由于您没有指定批处理中文档的插入顺序,因此 MongoDB 必须遵守插入按照指定顺序发生的要求。结果是当且仅当它们对应于同一个分片时,才能对连续插入进行批处理。

mongos 维护原始文档的顺序,因此只有属于同一个分片的连续插入可以组合在一起

例如。考虑“k”是分片键的情况。有两个分片,对应范围

[MinKey, 10], (20, MaxKey]

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

[{k: 1}, {k: 25}, {k: 2}]

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

没有两个连续的文档属于同一个分片,因此在这种情况下,每个文档之后都需要调用getLastError

在散列键的情况下,文档将更随机地分布在分片中。即属于相同分片的文档可能更分散,因此会产生更多的批次 分布越随机,批次的大小越小,总批次的数量越多,getLastError 的成本就越高这实际上意味着性能较差。

FIX:指定"ordered: false"

collection.bulkWrite(list, new BulkWriteOptions().ordered(false));

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


还有,

MongoClient mongoClient = new MongoClient(host, port);

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

在这种情况下,您的所有写入请求都将路由到单个节点,该节点负责由于分片集群而负责所有额外的簿记工作。你应该使用的是

MongoClient(final List<ServerAddress> seeds)

当根据请求类型(读取或写入)和读取偏好(如果它是 读请求),驱动程序会随机选择一个服务器发送一个 要求。这适用于副本集和分片集群。

注意:将尽可能多的服务器放入列表中,系统将 找出其余的。

【讨论】:

    【解决方案2】:

    一般来说,每当您使用分片解决方案时,您都需要考虑:

    1. 您的客户端应用程序将支持集群,因此能够自行执行路由
    2. 您的客户端应用程序将联系执行路由的中间节点

    我的怀疑是 Mongo Client 不是“自动”识别集群的,这意味着如果您不指定它们,它不会查找属于集群的节点。这种感觉通过以下方式得到加强:

    您可以使用 Java 驱动程序连接到副本集,方法是传递一个 MongoClient 构造函数的 ServerAddress 列表。例如:

    MongoClient mongoClient = new MongoClient(Arrays.asList( new 服务器地址(“本地主机”,27017),新服务器地址(“本地主机”, 27018), 新服务器地址("localhost", 27019)));

    您可以连接 使用相同的构造函数到分片集群。 MongoClient 将 自动检测服务器是副本集成员列表还是 mongos 服务器列表。

    【讨论】:

      猜你喜欢
      • 2012-02-25
      • 2015-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-30
      • 2014-10-28
      相关资源
      最近更新 更多