.NET MongoDB 连接的最佳实践?

Posted

技术标签:

【中文标题】.NET MongoDB 连接的最佳实践?【英文标题】:.NET best practices for MongoDB connections? 【发布时间】:2011-01-12 17:40:48 【问题描述】:

我最近一直在使用 GitHub 上的 C# 驱动程序玩 MongoDB(速度非常快)。在我正在测试的小型单线程控制台应用程序中,一切正常。我能够在运行单线程的 8 秒内添加 1,000,000 个文档(是的,百万个)。如果我在 for 循环范围之外使用连接,我只会获得这种性能。换句话说,我为每个插入保持连接打开,而不是为每个插入连接。显然这是人为的。

我想我会把它提高一个档次,看看它是如何与多线程一起工作的。我这样做是因为我需要模拟一个具有多个并发请求的网站。我在 15 到 50 个线程之间旋转,在所有情况下仍然插入总共 150,000 个文档。如果我只是让线程运行,每个线程为每个插入操作创建一个新连接,性能就会停止。

显然我需要找到一种方法来共享、锁定或池化连接。问题就在于此。连接到 MongoDB 的最佳实践是什么?连接是否应该在应用程序的整个生命周期内保持打开状态(每次操作打开和关闭 TCP 连接都有相当长的延迟)?

是否有人对 MongoDB 有任何实际或生产经验,特别是底层连接?

这是我的线程示例,它使用为插入操作而锁定的静态连接。请提供可以在网络环境中最大限度地提高性能和可靠性的建议!

private static Mongo _mongo;

private static void RunMongoThreaded()

    _mongo = new Mongo();
    _mongo.Connect();

    var threadFinishEvents = new List<EventWaitHandle>();

    for(var i = 0; i < 50; i++)
    
        var threadFinish = new EventWaitHandle(false, EventResetMode.ManualReset);
        threadFinishEvents.Add(threadFinish);

        var thread = new Thread(delegate()
            
                 RunMongoThread();
                 threadFinish.Set();
            );

        thread.Start();
    

    WaitHandle.WaitAll(threadFinishEvents.ToArray());
    _mongo.Disconnect();


private static void RunMongoThread()

    for (var i = 0; i < 3000; i++)
    
        var db = _mongo.getDB("Sample");
        var collection = db.GetCollection("Users");
        var user = GetUser(i);
        var document = new Document();
        document["FirstName"] = user.FirstName;
        document["LastName"] = user.LastName;

        lock (_mongo) // Lock the connection - not ideal for threading, but safe and seemingly fast
        
            collection.Insert(document);
        
    

【问题讨论】:

你最后做了什么决定?面临同样的问题... 好消息是我不必做决定。 Mongodb-csharp 和 NoRM 驱动程序都添加了对连接池的支持。这两个库都有精心设计的线程安全机制,用于针对 mongod 或 mongos 进程池连接。这两个领域也在不久的将来增加了对副本集的支持。 @TylerBrinks 你能举例说明如何在 8 秒内插入 1m 个文档吗?我无法在单线程上达到那个速度。 【参考方案1】:

这里的大多数答案都已过时并且不再适用,因为 .net 驱动程序已经成熟并添加了无数功能。

查看此处找到的新 2.0 驱动程序的文档: http://mongodb.github.io/mongo-csharp-driver/2.0/reference/driver/connecting/

.net 驱动程序现在是线程安全的并处理连接池。根据文档

建议将 MongoClient 实例存储在全局位置,作为静态变量或具有单例生命周期的 IoC 容器。

【讨论】:

这个答案需要发送到顶部。接受我的投票!【参考方案2】:

我正在使用 csharp-mongodb 驱动程序,但它对我的连接池没有帮助:(每个 Web 请求我对 mongodb 大约有 10-20 个请求。(150 个用户在线 - 平均)而且我什至无法监控统计数据或从 shell 连接到 mongodb 它会向我抛出异常。

我已经创建了存储库,它根据请求打开和处理连接。我依靠这样的东西: 1)驱动有连接池 2)经过我的研究(我在用户组中发布了一些关于此的问题) - 我了解到创建 mongo 对象和打开连接不会繁重的操作,所以繁重的操作。

但是今天我的产量下降了:( 可能我必须为每个请求保存打开的连接...

这里是用户组http://groups.google.com/group/mongodb-user/browse_thread/thread/3d4a4e6c5eb48be3#的链接

【讨论】:

如果你释放你的连接,你实际上是在与连接池对抗——连接池不能回收一个释放的连接,并且必须为每个请求建立一个全新的连接的开销。只需使用您的连接,完成后将其关闭。【参考方案3】:

在使用 mongodb-csharp 时,您可以像对待 ADO 连接一样对待它。 当你创建一个 Mongo 对象时,它会从池中借用一个连接,直到它被释放为止。因此,在 using 块之后,连接又回到了池中。 创建 Mongo 对象既便宜又快速。

示例

for(var i=0;i<100;i++) 
 
        using(var mongo1 = new Mongo()) 
        using(var mongo2 = new Mongo()) 
         
                mongo1.Connect(); 
                mongo2.Connect(); 
         
 

数据库日志从 127.0.0.1:58214 #1 接受的连接于 6 月 2 日星期三 20:54:21 6 月 2 日星期三 20:54:21 接受来自 127.0.0.1:58215 #2 的连接 6 月 2 日星期三 20:54:21 MessagingPort recv() errno:0 没有错误 127.0.0.1:58214 6 月 2 日星期三 20:54:21 结束连接 127.0.0.1:58214 6 月 2 日星期三 20:54:21 MessagingPort recv() errno:0 没有错误 127.0.0.1:58215 6 月 2 日星期三 20:54:21 结束连接 127.0.0.1:58215

注意它只打开了 2 个连接。

我使用 mongodb-csharp 论坛将这些放在一起。 http://groups.google.com/group/mongodb-csharp/browse_thread/thread/867fa78d726b1d4

【讨论】:

迄今为止的最佳答案。谢谢! :)【参考方案4】:

CSMongo, 是一个由 jLinq 的开发人员创建的 MongoDB 的 C# 驱动程序,但仍然令人感兴趣。这是一个示例:

//create a database instance
using (MongoDatabase database = new MongoDatabase(connectionString)) 

    //create a new document to add
    MongoDocument document = new MongoDocument(new 
        name = "Hugo",
        age = 30,
        admin = false
    );

    //create entire objects with anonymous types
    document += new 
        admin = true,
        website = "http://www.hugoware.net",
        settings = new 
            color = "orange",
            highlight = "yellow",
            background = "abstract.jpg"
        
    ;

    //remove fields entirely
    document -= "languages";
    document -= new[]  "website", "settings.highlight" ;

    //or even attach other documents
    MongoDocument stuff = new MongoDocument(new 
        computers = new []  
            "Dell XPS", 
            "Sony VAIO", 
            "Macbook Pro" 
            
        );
    document += stuff;

    //insert the document immediately
    database.Insert("users", document);


【讨论】:

【参考方案5】:

连接池应该是你的答案。

该功能正在开发中(详情请参阅http://jira.mongodb.org/browse/CSHARP-9)。

目前,对于 Web 应用程序,最佳做法是在 BeginRequest 处连接并在 EndRequest 处释放连接。但对我来说,我认为对于没有连接池的每个请求来说,操作太昂贵了。所以我决定拥有全局 Mongo 对象并将其用作每个线程的共享资源(如果你现在从 github 获得最新的 C# 驱动程序,它们也会提高一点并发性能)。

我不知道使用 Global Mongo 对象的缺点。所以让我们等待另一位专家对此发表评论。

但我认为在功能(连接池)完成之前我可以忍受它。

【讨论】:

您是否使用相同的方式连接到 SQL Server/mysql?我认为连接池的最佳实践仍然是“迟开早关”,并且在请求期间多次打开/关闭连接几乎没有任何成本。【参考方案6】:

关于静态连接要记住的一点是它在所有线程之间共享。你想要的是每个线程一个连接。

【讨论】:

您可能错过了我所说的每个线程一个连接明显慢的部分。我认为这不是高流量网站的最佳答案。 对于您的示例,您在其中对事物进行分组,每个线程一个是您能做的最好的。一个静态的共享连接像你看到的那样创建死锁。您的替代方法是进行连接池。这是 sql server 提供程序内置的东西,但对于 mongo,您必须自己构建,而且要做好并非易事。 今天再看这个,也有可能是你的线程太多了。理想情况下,您需要为您的工作项目创建一个共享的、线程安全的队列,并且只有少数几个线程(具体数量取决于您的系统,但最大的因素是处理器内核的数量)。每个线程从队列中拉取项目。这将减少连接的数量,因此它们不再是瓶颈。

以上是关于.NET MongoDB 连接的最佳实践?的主要内容,如果未能解决你的问题,请参考以下文章

Node.js 上 MongoDB 连接的最佳实践是啥?

MongoDB开发最佳实践

MongoDB进阶之路:不仅仅是技术研究,还有优化和最佳实践

mongodb最佳实践:筑巢

MongoDB / Mongoose 单元测试 - 最佳实践? [关闭]

Koa2+MongoDB+JWT实战--Restful API最佳实践