MongoDB C# 驱动程序 - 更新许多文档时出现性能问题
MongoDB C# driver - Performance issues when updating many documents
我需要大约每分钟更新一次 100,000 多个文档的字段,我发现我的 i7、6GB RAM、SSD HD PC 上的当前代码几乎没有
足够高效。据我了解,您无法使用驱动程序进行批量更新(我是 运行 来自 Nuget 的最新版本)
这是我通过 运行 我的代码获得的结果(执行 25,000 次更新的时间)如下:
- 从空集合开始 = 152 秒
- 从预填充集合开始 = 151 秒
- 带索引的预填充集合 = 6.5 秒
- 预填充索引和 FindOneAndReplaceAsync 方法 = 20 秒
正如预期的那样,索引效果最好,我不确定为什么异步效率较低。当将来增长到超过 100,000 updates/min 时,即使以这种方式建立索引也可能变得太慢。
这是 FindOneAndReplaceAsync 的预期行为吗?如果是,是否有另一种方法来获得更好的性能。我是不是想用 MongoDB 做一些不适合它的事情?
代码(MCVE 就绪):
public class A
{
public A(string id)
{
customId = id;
TimeStamp = DateTime.UtcNow;
}
[BsonId]
[BsonIgnoreIfDefault]
ObjectId Id { get; set; }
public string customId { get; set; }
public double val { get; set; }
public DateTime TimeStamp { get; set; }
}
class Program
{
static IMongoCollection<A> Coll = new MongoClient("mongodb://localhost").GetDatabase("Test").GetCollection<A>("A");
static FindOneAndReplaceOptions<A,A> Options = new FindOneAndReplaceOptions<A, A> { IsUpsert = true, };
static void SaveDoc(A doc)
{
Coll.FindOneAndReplace(Builders<A>.Filter.Where(x => x.customId == doc.customId), doc, Options);
}
static void Main(string[] args)
{
var docs = Enumerable.Range(0, 25000).Select(x => new A(x.ToString()));
Stopwatch sw = new Stopwatch();
sw.Start();
docs.ToList().ForEach(x => SaveDoc(x));
sw.Stop();
Debug.WriteLine(sw.ElapsedMilliseconds);
}
}
我认为问题与协议和网络延迟有关。每个 Update
操作都有序列化和传输损失。您可以使用批量写入来优化批处理操作性能。
在你的情况下它看起来像这样:
//create container for bulk operations
var operations = new List<WriteModel<BsonDocument>>();
//add batch tasks
operations.Add(new ReplaceOneModel<A>(new BsonDocument("customId", doc1.customId), doc1) { IsUpsert = true });
operations.Add(new ReplaceOneModel<A>(new BsonDocument("customId", doc2.customId), doc2) { IsUpsert = true });
//execute BulkWrite operation
collection.BulkWrite(operations, new BulkWriteOptions() { BypassDocumentValidation = true, IsOrdered = false });
我建议将每个 BulkWrite
操作的批量大小限制为不超过 1000 个文档。 MongoDb对BSON文件大小有限制(16MB),可能会导致运行失败。当然,对于只有几个字段的简单文档,批量大小可以是 10000 甚至更多。
BypassDocumentValidation
和 IsOrdered
选项也可以显着加快写入过程。
还有一件事...
您可以使用原始 BsonDocuments 而不是过滤器构建器来消除 LINQ 选择器处理和过滤器解析惩罚。
//instead of this
Builders<A>.Filter.Where(x => x.customId == doc.customId)
//you can use BsonDocument
new BsonDocument("customId", doc.customId)
在执行命令之前,您的过滤器构建器将被序列化为完全相同的 BSON 文档。
我需要大约每分钟更新一次 100,000 多个文档的字段,我发现我的 i7、6GB RAM、SSD HD PC 上的当前代码几乎没有 足够高效。据我了解,您无法使用驱动程序进行批量更新(我是 运行 来自 Nuget 的最新版本)
这是我通过 运行 我的代码获得的结果(执行 25,000 次更新的时间)如下:
- 从空集合开始 = 152 秒
- 从预填充集合开始 = 151 秒
- 带索引的预填充集合 = 6.5 秒
- 预填充索引和 FindOneAndReplaceAsync 方法 = 20 秒
正如预期的那样,索引效果最好,我不确定为什么异步效率较低。当将来增长到超过 100,000 updates/min 时,即使以这种方式建立索引也可能变得太慢。
这是 FindOneAndReplaceAsync 的预期行为吗?如果是,是否有另一种方法来获得更好的性能。我是不是想用 MongoDB 做一些不适合它的事情?
代码(MCVE 就绪):
public class A
{
public A(string id)
{
customId = id;
TimeStamp = DateTime.UtcNow;
}
[BsonId]
[BsonIgnoreIfDefault]
ObjectId Id { get; set; }
public string customId { get; set; }
public double val { get; set; }
public DateTime TimeStamp { get; set; }
}
class Program
{
static IMongoCollection<A> Coll = new MongoClient("mongodb://localhost").GetDatabase("Test").GetCollection<A>("A");
static FindOneAndReplaceOptions<A,A> Options = new FindOneAndReplaceOptions<A, A> { IsUpsert = true, };
static void SaveDoc(A doc)
{
Coll.FindOneAndReplace(Builders<A>.Filter.Where(x => x.customId == doc.customId), doc, Options);
}
static void Main(string[] args)
{
var docs = Enumerable.Range(0, 25000).Select(x => new A(x.ToString()));
Stopwatch sw = new Stopwatch();
sw.Start();
docs.ToList().ForEach(x => SaveDoc(x));
sw.Stop();
Debug.WriteLine(sw.ElapsedMilliseconds);
}
}
我认为问题与协议和网络延迟有关。每个 Update
操作都有序列化和传输损失。您可以使用批量写入来优化批处理操作性能。
在你的情况下它看起来像这样:
//create container for bulk operations
var operations = new List<WriteModel<BsonDocument>>();
//add batch tasks
operations.Add(new ReplaceOneModel<A>(new BsonDocument("customId", doc1.customId), doc1) { IsUpsert = true });
operations.Add(new ReplaceOneModel<A>(new BsonDocument("customId", doc2.customId), doc2) { IsUpsert = true });
//execute BulkWrite operation
collection.BulkWrite(operations, new BulkWriteOptions() { BypassDocumentValidation = true, IsOrdered = false });
我建议将每个 BulkWrite
操作的批量大小限制为不超过 1000 个文档。 MongoDb对BSON文件大小有限制(16MB),可能会导致运行失败。当然,对于只有几个字段的简单文档,批量大小可以是 10000 甚至更多。
BypassDocumentValidation
和 IsOrdered
选项也可以显着加快写入过程。
还有一件事...
您可以使用原始 BsonDocuments 而不是过滤器构建器来消除 LINQ 选择器处理和过滤器解析惩罚。
//instead of this
Builders<A>.Filter.Where(x => x.customId == doc.customId)
//you can use BsonDocument
new BsonDocument("customId", doc.customId)
在执行命令之前,您的过滤器构建器将被序列化为完全相同的 BSON 文档。