【问题标题】:Speeding up LINQ to EF queries to max将 LINQ to EF 查询加速到最大
【发布时间】:2017-01-16 13:39:04
【问题描述】:

我有一个包含大量记录的数据库表,我预计它会在某个时间点上升到 9 亿 - 10 亿条记录。

我想弄清楚的是如何加快我的 LINQ to SQL 查询,目前看起来像这样:

var allProducts = ctx.ProductsTransactions
                     .Where(x => x.SearchedUserID == User.SearchedUserID)
                     .ToList();

ctx 对象是我的实体对象(我的数据库的映射类)。

某些用户最多可以存储 25000 条记录,查询有时可能需要 10-15 秒才能显示记录。

附:我从数据库中检索的数据应该并且是只读的,不能以任何方式进行操作。

有什么办法可以加快这个查询的速度吗?

编辑:

好的,所以我在控制器中定义了我的实体对象,如下所示:

public db_Entity ctx = new db_Entity();

从数据库中提取数据后,我将其分组如下:

var prepared = allProducts.ToList();
var filteredProducts = prepared.GroupBy(x => x.ItemID).Select(x => new ResultItem()
{
    ID = x.Key,
    SaleNumber = x.Select(y => y.QuantityPurchased).Sum(),
    SaleEarning = x.Select(y => y.QuantityPurchased * y.SalePrice).Sum(),
    Title = x.Select(y => y.Title).FirstOrDefault(),
    CurrentPrice = x.OrderByDescending(y=>y.TransactionDate).Select(y=>y.SalePrice).FirstOrDefault(),
    GalleryURL = "http://somesite.com",
    SalePrice = x.Select(y => y.SalePrice).FirstOrDefault()
}).ToList();

之后,我将过滤后的产品放入 viewbag 中,这样我就可以将其展示给最终用户,如下所示:

ViewBag.Products = filteredProducts;

你们需要更多关于表结构的信息吗?

编辑 #2:伙计们,你们中的许多人都提到我应该在我的 SQL 表中对 SearchedUserId 实施索引...我该怎么做?

【问题讨论】:

  • 有上百万种方法可以加快速度,但我们需要更多地了解您的数据库/代码/数据/服务器/等。我们现在真正能做的就是确保您在SearchedUserID 列上有一个索引。
  • 我的意思是这个网站不是问这种问题的好地方。我们需要非常具体的编程问题,而性能问题通常非常笼统。
  • @User987 您使用的是 LINQ to SQL 还是 LINQ to EF?如果 LINQ to SQL 无法将您写入的内容转换为 SQL,它会将所有内容加载到内存中。不要使用它。此外,简化您的查询。 LINQ 不是 SQL 的替代品。如果一个查询在 SQL 或 LINQ 中看起来很丑陋,那么它的性能就会很差。您必须检查生成的 SQL 语句以查看实际执行的内容
  • 你应该考虑在服务器上实现分页,这样你就不必在页面加载时加载25k行,每次加载10、20或50行。
  • 你也可以用这个x.Sum(y => y.QuantityPurchased)替换这个x.Select(y => y.QuantityPurchased).Sum()

标签: c# asp.net sql-server asp.net-mvc linq


【解决方案1】:

几乎每个人都提到过,首先想到的是在 SQL 中为 SearchedUserId 列添加索引:

CREATE NONCLUSTERED INDEX [idx_SearchedUserId] 
ON ProductsTransactions ([SearchedUserId] ASC|DESC) 
ON [filegroup_or_partition_name]

还有一些其他选项可以提高性能,例如微调您的服务器平台、硬件等(使用闪存也可以显着提高性能)。

【讨论】:

  • 太棒了,为这个! :) 我应该把什么作为文件组或分区名称?分区名称如“C 盘”或?
  • 当您创建 SQL 数据库时,您定义了一个文件组或分区名称(或者可以保留为默认名称)。您必须检查您的数据库属性才能找到它。
【解决方案2】:

不要忽视优化应用的这一部分,不要使用 ORM 工具。

ADO.Net 命令将更快并消除任何开销,无论是使用 SQL 查询还是将参数传递给存储过程。

另外,请考虑是否可以在应用中缓存数据以消除对数据库命中的需要。

【讨论】:

  • 虽然使用 ORM 与不使用 ORM 之间存在性能差异,但与正确设置数据库和正确编写查询相比,我一直发现它微不足道。只有当 ORM 不支持您可以在 SQL 中使用的 db 功能时,我才发现有必要。
  • @Mant101,根据经验,EF6 比使用 Dapper 执行的手动查询花费了大约 200 毫秒,这个数量级。如果您关心 200 毫秒,则值得考虑。正如您所暗示的,一个写得不好的查询可能会花费 10 秒的时间,但是,您通常需要控制将要执行的确切查询。概括地说,性能越重要,您就越不想使用生成 SQL 的 ORM。
  • 是第一次运行查询,还是 EF 缓存了表达式的后续时间?我的 EF 查询一直快于 200 毫秒,所以多 200 毫秒似乎是可疑的。我曾经花很长时间优化数据库调用,老实说从 linq 切换到原始 sql 几乎从来都不是解决方案。
  • 确实,但是orm生成的SQL可能效率低,应该提一下。
【解决方案3】:

如果您希望优化此特定查询,那么您需要一个与您的扩展查询完全匹配的覆盖索引,例如

CREATE NONCLUSTERED INDEX [IX_ProductsTransactions_SearchedUserId_ItemId_etc]
    ON [ProductsTransactions]
    (
        [SearchedUserId] ASC,
        [ItemId] ASC,
        [TransactionDate] DESC,
        [SalePrice] ASC,
        [Title] ASC
    )

这将最大化已定义查询的性能,但会限制索引对其他查询的可重用性。


但是,如果数据是真正只读的并且永远不会更改,并且由于您的查询不包含变量,那么为什么不预先计算结果并直接从应用程序层返回该数据而无需调用数据库。这样会更快。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-03
    相关资源
    最近更新 更多