Moh*_*ang 9 java performance bulkinsert mongodb
我有一个Mongodb群集,其中包含一个主副本和一个副本副本作为复制集一起运行.但随着流量的增长,我决定执行分片以获得更高的写入速度.
我根据本教程对"_id"列执行了散列分片,并将数据拆分为两个分片.然后,我进行了一些基准测试,发现在某些情况下,分片群集甚至比未散列群集更慢.
这是测试结果.
最大吞吐量测试:使用十台机器同时运行"mongoimport"将数据加载到目标数据库中,以测试db的最大写入速度.
结果:
分片群集可以插入39500个文档/秒.
未整数群集可以插入27400个文档/秒.
单实例mongoimport测试:只使用一台机器运行"mongoimport"将数据加载到目标数据库中.
结果:
分片群集可以插入14285个文档/秒.
未整数群集可以插入14085个文档/秒.
使用mongodb java驱动程序加载单实例数据:通过调用mongodb java驱动程序的api,只使用一个实例将数据加载到目标数据库中.
结果:
分片群集可以插入4630个文档.
未整数群集可以插入17544个文档/秒.
第一次测试的结果非常有意义.您将数据库分成2个分片群集,吞吐量增加了约50%,一切都很完美,万岁!
第二次测试有点道理.那么吞吐量大致相同,但可能是数据加载器方面的瓶颈,毕竟我们只用一个实例加载数据.
但第三次测试确实让我感到困惑.没有意义的是,分片群集可以比未受干扰的群集慢得多.另一方面,unsharded db具有惊人的速度,甚至比使用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();
}
Run Code Online (Sandbox Code Playgroud)
这是可能的罪魁祸首 collection.bulkWrite(list);
在进行批量写入的情况下,mongos需要将您的批处理分解成较小的批处理,并分到每个分片。
由于您尚未指定批处理中文档插入顺序的任何内容,因此MongoDB必须遵守插入按指定顺序进行的要求。结果是,当且仅当连续的插入物对应于相同的分片时,才能对连续的插入物进行批处理。
mongos保持原始文档顺序,因此只能将属于同一分片的连续插入分组到一起
例如。考虑“ k”是分片键的情况。有两个分片,对应范围
[MinKey, 10], (20, MaxKey]
Run Code Online (Sandbox Code Playgroud)
现在假设我们批量插入以下文档:
[{k: 1}, {k: 25}, {k: 2}]
Run Code Online (Sandbox Code Playgroud)
Doc1-> Shard1,Doc2-> Shard2,Doc3-> Shard3
没有两个连续的文档属于同一个分片,因此在这种情况下,每个文档之后都需要调用getLastError。
对于散列密钥,文档将在碎片之间更随机地分布。也就是说,属于相同分片的文档可能会更加分散,因此将创建更多的批次。分布越随机,批次的大小越小,批次总数越多,则产生的成本越高,getLastError这实际上意味着性能越差。
FIX:指定"ordered: false"。
collection.bulkWrite(list, new BulkWriteOptions().ordered(false));
Run Code Online (Sandbox Code Playgroud)
这告诉数据库您不关心严格保留插入顺序。使用"ordered: false",mongos将为每个分片创建一个批处理,从而避免了额外的getLastError调用。每个批处理操作可以在适当的分片上同时执行,而无需等待getLastError前一个批处理的响应。
也,
MongoClient mongoClient = new MongoClient(host, port);
基于单个mongodb节点创建Mongo实例,并且将无法发现副本集或分片群集中的其他节点。
在这种情况下,您的所有写请求都将路由到单个节点,该节点由于分片群集而负责所有其他簿记工作。您应该使用的是
MongoClient(final List<ServerAddress> seeds)
Run Code Online (Sandbox Code Playgroud)
当根据请求类型(读取或写入)和读取首选项(如果是读取请求)从多个服务器中选择时,驱动程序将随机选择一个服务器来发送请求。这适用于副本集和分片群集。
注意:在列表中放置尽可能多的服务器,系统将找出其余的服务器。
一般来说,每当您使用分片解决方案时,您都需要考虑:
我怀疑 Mongo Client 不是“自动”感知集群的,这意味着如果您不指定属于集群的节点,它不会查找它们。这种感觉通过以下因素得到加强:
您可以通过将 ServerAddress 列表传递给 MongoClient 构造函数,使用 Java 驱动程序连接到副本集。例如:
MongoClient mongoClient = new MongoClient(Arrays.asList( new ServerAddress("localhost", 27017), new ServerAddress("localhost", 27018), new ServerAddress("localhost", 27019)));
您可以使用相同的构造函数连接到分片集群。MongoClient 将自动检测服务器是副本集成员列表还是 mongos 服务器列表。