MongoDB C#驱动程序:API与Linq性能

本文关键字:Linq 性能 API 驱动程序 MongoDB | 更新日期: 2023-09-27 18:28:58

我正在尝试制作一个带有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();
}

据我所知,Filter定义是您通常将查询编码到DB的方式。

以下是从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不应该包含技术相关的classess(如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

MongoDB C#驱动程序:API与Linq性能

您最初的问题是为什么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更改为另一个数据源(SQLServer/Oracle/MySQL/XML/等),而无需更改太多代码,但您只需为这种抽象付出一点性能上的代价。