我有一个Mongodb群集,其中包含一个主副本和一个副副本一起作为复制集运行。但随着流量的增长,我决定执行分片以获得更高的写入速度。
我根据此tutorial对“_id”列执行了散列分片,并将数据拆分为两个分片。然后,我进行了一些基准测试,发现在某些情况下,分片群集甚至比未钻孔群集慢。
这是测试结果。
最大吞吐量测试:使用10台机器同时运行“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();
}
答案 0 :(得分:3)
这可能是罪魁祸首 collection.bulkWrite(list);
如果是批量写入,mongos需要将您的批次分成更小的批次,这些批次会转到每个分片。
由于您尚未指定批处理中文档的插入顺序,MongoDB必须遵守插入按指定顺序发生的要求。结果是,当且仅当它们对应于相同的分片时,才能对连续插入进行批处理。
mongos维护原始文档顺序,因此只有属于同一分片的连续插入才能组合在一起
例如。考虑一下&#34; k&#34;是分片键。有两个分片,对应于范围
[MinKey, 10], (20, MaxKey]
现在假设我们批量插入以下文件:
[{k: 1}, {k: 25}, {k: 2}]
Doc1 - &gt; Shard1,Doc2 - &gt; Shard2,Doc3 - &gt; Shard3
没有两个连续文档属于同一个分片,因此在这种情况下,每个文档后都需要调用getLastError。
在Hashed键的情况下,文档将在分片中更随机地分发。即属于相同分片的文档可能更分散,因此会产生更多批次分布越随机,批量越小,批次总数越多,{{1这实际上意味着性能更差。
FIX :指定getLastError
。
"ordered: false"
这告诉数据库您不关心严格保留插入的顺序。使用collection.bulkWrite(list, new BulkWriteOptions().ordered(false));
,mongos将为每个分片创建一个批处理,从而避免额外的getLastError调用。可以在适当的分片上同时执行每个批处理操作,而无需等待前一批的"ordered: false"
响应。
另外,
getLastError
基于单个mongodb节点创建Mongo实例,并且无法发现副本集或分片群集中的其他节点。
在这种情况下,所有写入请求都将路由到单个节点,由于分片群集,该节点负责所有额外的簿记工作。您应该使用的是
MongoClient mongoClient = new MongoClient(host, port);
根据请求类型(读取或写入)和读取首选项(如果它是一个),可以选择多个服务器 读取请求),驱动程序将随机选择要发送的服务器 请求。这适用于副本集和分片集群。
注意:在列表中放置尽可能多的服务器,系统将会 弄清楚剩下的。
答案 1 :(得分:0)
一般来说,无论何时使用分片解决方案,您都需要考虑:
我的怀疑是Mongo客户端“自动”不支持群集,这意味着如果您不指定它们,它不会查找属于群集的节点。这种感觉通过以下方式得到加强:
您可以通过传递使用Java驱动程序连接到副本集 ServerAddress列表到MongoClient构造函数。例如:
MongoClient mongoClient =新的MongoClient(Arrays.asList(new ServerAddress(“localhost”,27017),新的ServerAddress(“localhost”, 27018),新的ServerAddress(“localhost”,27019)));
你可以连接 使用相同的构造函数到分片集群。 MongoClient会 自动检测服务器是否是副本集成员列表或a mongos服务器列表。