.NET MongoDB 连接最佳实践?

发布于 2024-08-20 00:15:37 字数 1697 浏览 9 评论 0原文

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

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

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

有没有人有 MongoDB 的实际或生产经验,特别是底层连接?

这是我的线程示例,使用针对插入操作锁定的静态连接。请提供可最大限度提高 Web 环境中的性能和可靠性的建议!

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);
        }
    }
}

I've been playing with MongoDB recently (It's AMAZINGLY FAST) using the C# driver on GitHub. Everything is working just fine in my little single threaded console app that I'm testing with. I'm able to add 1,000,000 documents (yes, million) in under 8 seconds running single threaded. I only get this performance if I use the connection outside the scope of a for loop. In other words, I'm keeping the connection open for each insert rather than connecting for each insert. Obviously that's contrived.

I thought I'd crank it up a notch to see how it works with multiple threads. I'm doing this because I need to simulate a website with multiple concurrent requests. I'm spinning up between 15 and 50 threads, still inserting a total of 150,000 documents in all cases. If I just let the threads run, each creating a new connection for each insert operation, the performance grinds to a halt.

Obviously I need to find a way to share, lock, or pool the connection. Therein lies the question. What's the best practice in terms of connecting to MongoDB? Should the connection be kept open for the life of the app (there is substantial latency opening and closing the TCP connection for each operation)?

Does anyone have any real world or production experience with MongoDB, and specifically the underlying connection?

Here is my threading sample using a static connection that's locked for insert operations. Please offer suggestions that would maximize performance and reliability in a web context!

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);
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

水染的天色ゝ 2024-08-27 00:15:37

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

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

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

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

Most answers here are outdated and are no longer applicable as the .net driver has matured and had numberless features added.

Looking at the documentation of the new 2.0 driver found here:
http://mongodb.github.io/mongo-csharp-driver/2.0/reference/driver/connecting/

The .net driver is now thread safe and handles connection pooling. According to documentation

It is recommended to store a MongoClient instance in a global place, either as a static variable or in an IoC container with a singleton lifetime.

罪歌 2024-08-27 00:15:37

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

The thing to remember about a static connection is that it's shared among all your threads. What you want is one connection per thread.

南笙 2024-08-27 00:15:37

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

示例

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

数据库日志
6 月 2 日星期三 20:54:21 从 127.0.0.1:58214 #1 接受连接
6 月 2 日星期三 20:54:21 从 127.0.0.1:58215 #2 接受连接
六月 2 日星期三 20:54:21 MessagingPort recv() errno:0 无错误 127.0.0.1:58214
六月 2 日星期三 20:54:21 结束连接 127.0.0.1:58214
六月 2 日星期三 20:54:21 MessagingPort recv() errno:0 无错误 127.0.0.1:58215
Wed Jun 02 20:54:21 结束连接 127.0.0.1:58215

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

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

When using mongodb-csharp you treat it like you would an ADO connection.
When you create a Mongo object it borrows a connection from the pool, which it owns until it is disposed. So after the using block the connection is back into the pool.
Creating Mongo objects are cheap and fast.

Example

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

Database Log
Wed Jun 02 20:54:21 connection accepted from 127.0.0.1:58214 #1
Wed Jun 02 20:54:21 connection accepted from 127.0.0.1:58215 #2
Wed Jun 02 20:54:21 MessagingPort recv() errno:0 No error 127.0.0.1:58214
Wed Jun 02 20:54:21 end connection 127.0.0.1:58214
Wed Jun 02 20:54:21 MessagingPort recv() errno:0 No error 127.0.0.1:58215
Wed Jun 02 20:54:21 end connection 127.0.0.1:58215

Notice it only opened 2 connections.

I put this together using mongodb-csharp forum.
http://groups.google.com/group/mongodb-csharp/browse_thread/thread/867fa78d726b1d4

温馨耳语 2024-08-27 00:15:37

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

//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);

}

Somewhat but still of interest is CSMongo, a C# driver for MongoDB created by the developer of jLinq. Here's a sample:

//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);

}
Oo萌小芽oO 2024-08-27 00:15:37

连接池应该是您的答案。

该功能正在开发中(请参阅 http://jira.mongodb.org/browse/CSHARP- 9 了解更多详细信息)。

目前,对于 Web 应用程序,最佳实践是在 BeginRequest 处连接并在 EndRequest 处释放连接。但对我来说,我认为对于没有连接池的每个请求来说,操作成本太高。因此,我决定拥有全局 Mongo 对象,并将其用作每个线程的共享资源(如果您现在从 github 获取最新的 C# 驱动程序,它们还会稍微提高并发性能)。

我不知道使用 Global Mongo 对象的缺点。那么我们就等待另一位专家对此的评论吧。

但我认为我可以忍受它,直到功能(连接池)完成。

Connection Pool should be your answer.

The feature is being developed (please see http://jira.mongodb.org/browse/CSHARP-9 for more detail).

Right now, for web application, the best practice is to connect at the BeginRequest and release the connection at EndRequest. But to me, I think that operation is too expensive for each request without Connection Pool. So I decide to have the global Mongo object and using that as shared resource for every threads (If you get the latest C# driver from github right now, they also improve the performance for concurrency a bit).

I don't know the disadvantage for using Global Mongo object. So let's wait for another expert to comment on this.

But I think I can live with it until the feature(Connection pool) have been completed.

撩发小公举 2024-08-27 00:15:37

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

我创建了存储库,它打开并处理每个请求的连接:
1)驱动程序有连接池
2)经过我的研究(我在用户组中发布了一些关于此的问题) - 我了解到创建 mongo 对象并打开连接并不需要繁重的操作,所以繁重的操作。

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

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

I am using csharp-mongodb driver and it doesn't help me with his connection pool :( I have about 10-20 request to mongodb per web request.(150 users online - average) And i can't even monitor statistics or connect to mongodb from shell it throw exception to me.

I have created repository, which open and dispose connection per request. I rely on such things as:
1) Driver has connection pool
2) After my research(i have posted some question in user groups about this) - i understood that creating mongo object and open connection doesn't heavy operation, so heavy operation.

But today my production go down :(
May be i have to save open connection per request...

here is link to user group http://groups.google.com/group/mongodb-user/browse_thread/thread/3d4a4e6c5eb48be3#

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文