【问题标题】:MongoDB C# Driver: API vs Linq PerformanceMongoDB C# 驱动程序:API 与 Linq 性能
【发布时间】:2016-01-06 00:12:11
【问题描述】:

我正在尝试使用 IRepository 模式(C#、MVC5)制作一个 MongoDB Web 应用程序,以使其更容易进行单元测试。只是想知道是否有人可以告诉我为什么这更快。

这是使用最新的 MongoDB c# 驱动程序。

在我的 IRepository 类中,我有以下内容

IQueryable<T> SearchFor();

List<T> SearchFor(FilterDefinition<T> filter);

发现一个 SO 帖子建议使用 IQueryable 来提高速度而不是使用 IEnumerable。

这是来自 MongoRepository 类的代码。

public IQueryable<T> SearchFor() {
    return _collection.AsQueryable<T>();
}

public List<T> SearchFor(FilterDefinition<T> filter) {
    return _collection.Find(filter).ToList();
}

据我所知,过滤器定义是您通常如何编写对 DB 的查询的代码。

这是从数据库中获取数据的调用

IQueryable<Client> asd4 = collection.SearchFor().Where(x => x.ClientDesc == "<search text>");

FilterDefinition<Client> filter1 = Builders<Client>.Filter.Eq("ClientDesc", "<search text>");
List<Client> asd10 = collection.SearchFor(filter1).ToList<Client>();

请注意,我知道我可能应该使用 IQueryable 和 Linq 路由,只是因为 IRepository 不应包含依赖于技术的类(如 FilterDefinition)。

当针对包含 30k 个简单文档的集合进行测试并测试不同方法的速度时,我得到以下结果。

使用 IQueryable 在 3 毫秒内完成,而 FilterDefinition 在 43 毫秒内完成。

我想知道为什么 IQueryable 上的 Linq 查询比使用 API 发送请求来返回特定值更快?


更新:根据@lenkan 的建议,我为 IQueryable 的每个循环添加了一个。

public void PerformanceTest(IRepository<Client> collection) {
    Stopwatch sw = new Stopwatch();

    // Delete all records
    // ******************
    System.Diagnostics.Debug.WriteLine("*****************");

    sw.Start();
    collection.DeleteAll();
    sw.Stop();
    System.Diagnostics.Debug.WriteLine("Deleting all records: " + sw.Elapsed);


    // Create 30k Records
    // ******************
    System.Diagnostics.Debug.WriteLine("*****************");

    sw.Reset();
    sw.Start();
    // Create 30k records
    for (int i = 0; i < 30000; i++) {
        Client testclient = new Client() {
            ClientDesc = "hahahahahahahahah " + i
        };
        collection.Add(testclient);
    }
    sw.Stop();
    System.Diagnostics.Debug.WriteLine("Created: 30k rows: " + sw.Elapsed);


    // Test IQueryable & LINQ
    // **********************
    System.Diagnostics.Debug.WriteLine("*********************");
    System.Diagnostics.Debug.WriteLine("* IQueryable & LINQ *");
    System.Diagnostics.Debug.WriteLine("*********************");

    sw.Reset();
    sw.Start();
    IQueryable<Client> asd4 = collection.SearchFor().Where(x => x.ClientDesc == "hahahahahahahahah 10");
    foreach (Client item in asd4) {
        string aaaaaa = item.ClientDesc;
    }
    sw.Stop();
    System.Diagnostics.Debug.WriteLine("Find one from start: " + sw.Elapsed);

    sw.Reset();
    sw.Start();
    IQueryable<Client> asd7 = collection.SearchFor().Where(x => x.ClientDesc == "hahahahahahahahah 10");
    foreach (Client item in asd7) {
        string aaaaaa = item.ClientDesc;
    }
    sw.Stop();
    System.Diagnostics.Debug.WriteLine("Find one from start: " + sw.Elapsed);

    sw.Reset();
    sw.Start();
    IQueryable<Client> asd5 = collection.SearchFor().Where(x => x.ClientDesc == "hahahahahahahahah 29999");
    foreach (Client item in asd5) {
        string bbbbbb = item.ClientDesc;
    }
    sw.Stop();
    System.Diagnostics.Debug.WriteLine("Find one from end: " + sw.Elapsed);

    sw.Reset();
    sw.Start();
    for (int i = 10000; i < 10050; i++) {
        IQueryable<Client> asd6 = collection.SearchFor().Where(x => x.ClientDesc == "hahahahahahahahah " + i);
        foreach (Client item in asd6) {
            string aaaaaa = item.ClientDesc;
        }
    }
    sw.Stop();
    System.Diagnostics.Debug.WriteLine("Find in loop of 50: " + sw.Elapsed);


    // Test Filter & LINQ
    // ***********************
    System.Diagnostics.Debug.WriteLine("*****************");
    System.Diagnostics.Debug.WriteLine("* List & Filter *");
    System.Diagnostics.Debug.WriteLine("*****************");

    sw.Reset();
    sw.Start();
    FilterDefinition<Client> filter1 = Builders<Client>.Filter.Eq("ClientDesc", "hahahahahahahahah 10");
    List<Client> asd10 = collection.SearchFor(filter1).ToList<Client>();
    foreach (Client item in asd10) {
        string aaaaaa = item.ClientDesc;
    }
    sw.Stop();
    System.Diagnostics.Debug.WriteLine("Find one from start: " + sw.Elapsed);

    sw.Reset();
    sw.Start();
    FilterDefinition<Client> filter2 = Builders<Client>.Filter.Eq("ClientDesc", "hahahahahahahahah 29999");
    List<Client> asd11 = collection.SearchFor(filter2).ToList<Client>();
    foreach (Client item in asd11) {
        string cccccc = item.ClientDesc;
    }
    sw.Stop();
    System.Diagnostics.Debug.WriteLine("Find one from end: " + sw.Elapsed);

    sw.Reset();
    sw.Start();
    for (int i = 10000; i < 10050; i++) {
        FilterDefinition<Client> filter3 = Builders<Client>.Filter.Eq("ClientDesc", "hahahahahahahahah " + i);
        List<Client> asd12 = collection.SearchFor(filter3).ToList<Client>();
    }
    sw.Stop();
    System.Diagnostics.Debug.WriteLine("Find in loop of 50: " + sw.Elapsed);

    // Delete all records
    // ******************
    System.Diagnostics.Debug.WriteLine("*****************");

    sw.Start();
    collection.DeleteAll();
    sw.Stop();
    System.Diagnostics.Debug.WriteLine("Deleting all records: " + sw.Elapsed);

}

这是现在的结果。因此,使用 IQueryable 进行枚举似乎对性能有初步影响,但是当您调用以后的搜索时,事情似乎会加速,即

*****************
Deleting all records: 00:00:00.0670336
*****************
Created: 30k rows: 00:00:04.6829844
*********************
* IQueryable & LINQ *
*********************
Find one from start: 00:00:00.0878309
Find one from start: 00:00:00.0120098
Find one from end: 00:00:00.0116334
Find in loop of 50: 00:00:00.5890532
*****************
* List & Filter *
*****************
Find one from start: 00:00:00.0248407
Find one from end: 00:00:00.0118345
Find in loop of 50: 00:00:00.5377828
*****************
Deleting all records: 00:00:00.7029368

【问题讨论】:

  • 有趣。您能否提供用于基准测试的代码的最小示例?从您提供的调用中,您永远不会枚举 asd4 IQueryable。
  • 添加了对原帖的回复。
  • 我仍然看到使用 Linq 而不是 FilterDefinitions 作为从界面中删除技术特定类的方法有很大的好处。尽管性能受到轻微影响(它将主要是管理后端)。

标签: c# mongodb linq mongodb-.net-driver


【解决方案1】:

您最初的问题是为什么 LINQ 比使用 API 快得多。该问题的答案是因为 LINQ 被延迟(延迟)执行,并且查询实际上并未完成。在您实际尝试迭代结果(foreach/.ToList()/etc)之前,查询不会完成。

你很可能是在这个陈述中计时的:

IQueryable<Client> asd4 = collection.SearchFor().Where(x => x.ClientDesc == "<search text>");

什么时候你应该给这个语句计时:

List<Client> asd4 = collection.SearchFor().Where(x => x.ClientDesc == "<search text>").ToList();

您在更新期间显示的性能数据似乎是合理的。 LINQ 实际上比使用直接 API 稍慢,因为它为查询添加了抽象。这种抽象将允许您轻松更改 MongoDB 以用于另一个数据源(SQL Server/Oracle/MySQL/XML/等),而无需进行很多代码更改,但您为这种抽象付出了代价,但性能会受到轻微影响。

【讨论】:

  • 可以在执行查询时分享实际的 Find 与 IQueryable 基准吗?
猜你喜欢
  • 1970-01-01
  • 2016-03-13
  • 2013-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多