我有一个Mongodb集群,它包含一个主副本和一个二级副本,作为一个复制集一起运行。但是随着流量的增加,我决定进行切分,以获得更高的写入速度。
我基于这个_id执行了对“教程”列的散列切分,并将数据分割成两个碎片。然后,我执行了一些基准测试,发现在某些情况下,切分的集群比未分割的集群还要慢。
这是测试结果。
第一个测试的结果是完全合理的。你将数据库分割成一个2片集群,吞吐量增加了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();
}发布于 2017-03-10 10:37:16
以下是可能的罪魁祸首collection.bulkWrite(list);
在大批量书写的情况下,芒果需要将你的批次分解成更小的批次,分给每个碎片。
由于您还没有指定有关批处理中文档的插入顺序的任何内容,所以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)当有多个服务器可根据请求类型(读或写)和读首选项(如果是读取请求)选择时,驱动程序将随机选择一个服务器发送请求。这既适用于复制集,也适用于切分群集。 注意:将尽可能多的服务器放到列表中,系统会找到其余的服务器。
发布于 2017-03-09 21:16:42
一般来说,无论何时使用切分解决方案,您都需要考虑以下两种方法之一:
我的怀疑是Mongo客户端不是“自动”集群感知的,这意味着如果您不指定属于集群的节点,它就不会查找它们。这种感觉因以下几点而得到加强:
您可以通过将ServerAddress列表传递给MongoClient构造函数,从而使用MongoClient驱动程序连接到副本集。例如: MongoClient mongoClient =新MongoClient(Arrays.asList(新ServerAddress("localhost",27017)、新ServerAddress("localhost",27018)、新ServerAddress("localhost",27019));您可以使用相同的构造函数连接到分片集群。MongoClient将自动检测服务器是复制集成员列表还是mongos服务器列表。
https://stackoverflow.com/questions/42522158
复制相似问题