在 EF6 中执行 StoredProcedure 需要花费大量时间

Posted

技术标签:

【中文标题】在 EF6 中执行 StoredProcedure 需要花费大量时间【英文标题】:Executing StoredProcedure takes incredible amount of time in EF6 【发布时间】:2018-12-28 11:14:13 【问题描述】:

我在 ASP.NET MVC5 中运行 EF6 (v6.2.0)。

当通过 EF6 的 SqlQuery() 函数执行某个 StoredProcedure 时,我必须等待大约 2 分钟(!)才能在内存中获取结果。

由于一些复杂的计算,StoredProcedure 在数据库中大约需要 9 - 12 秒,并使用 11 个参数调用:

exec sp_Calculation @q, @y, @gn, @gesa, @rg, @cl, @yc, @vlv, @vlb, @ugv, @ugb

结果是大约 2.1 MB 的数据(~9000 行,49 列)。

总执行时间:00:00:11.711

在代码中我这样称呼它:

dbContext.Database.Log = s => Trace.Write(s);
return await dbContext.Database.SqlQuery<CalculationResult>("exec sp_Calculation @q, @y, @gn, @gesa, @rg, @cl, @yc, @vlv, @vlb, @ugv, @ugb", parameters).ToListAsync(token);

追踪:

exec sp_Calculation @q, @y, @gn, @gesa, @rg, @cl, @yc, @vlv, @vlb, @ugv, @ugb

-- @q: 'null' (Type = Int32, IsNullable = false)
-- @y: '1101' (Type = Int16, IsNullable = false)
-- @gn: 'null' (Type = Int32, IsNullable = false)
-- @gesa: '1' (Type = Byte, IsNullable = false)
-- @rg: 'null' (Type = Int32, IsNullable = false)
-- @cl: '4' (Type = Byte, IsNullable = false)
-- @yc: '17' (Type = Int16, IsNullable = false)
-- @vlv: 'null' (Type = Int16, IsNullable = false)
-- @vlb: 'null' (Type = Int16, IsNullable = false)
-- @ugv: 'null' (Type = Int16, IsNullable = false)
-- @ugb: 'null' (Type = Int16, IsNullable = false)
-- Executing asynchronously at 19.07.2018 18:27:23 +02:00
-- Completed in 114479 ms with result: SqlDataReader

我的第一个猜测是网络是瓶颈,但是通过 SSMS 调用 Web 服务器上的 StoredProc 也非常快。所以网络应该不是问题。

这是来自 dotTrace 的调用堆栈,其中存在很大瓶颈: 奇怪的是原生程序集的执行时间非常长。

有人可以澄清那里到底发生了什么以及如何解决这个问题吗?

编辑: 我刚刚找到了一个有类似问题的question,并将尝试了解更多有关它的信息。也许它网络。

编辑 2: 由于在从中创建 csv 文件之前进行了一些预处理,我确实需要内存中的所有数据。瓶颈似乎在SNINativeMethodWrapper。我不需要帮助就可以使用其他库执行我的任务。我只是想在内存中更快地获取数据。

【问题讨论】:

ORM 不适用于报告查询。等待 9K 行返回,将它们转换为列表并然后使用它们也很慢,并且可能会导致大量重新分配。使用 IEnumerable 开始处理结果。毕竟,对于报告或导出,您不需要所有数据,您可以在第一行到达后立即开始写出结果。 你也可以使用像 Dapper 这样的 microORM。由于您不处理实体,因此您不需要更改跟踪、更新支持以及完整 ORM 提供的所有其他功能。只是将 DbDataReader 行映射到 CalculationResult 对象 我们在整个应用程序中使用 EF6 并且几乎使用了它的所有功能。所以现在换掉不是一个选择。问题是,加载数据后会有更多的处理。但是单独加载需要很长时间。 EF6 在显示 Trace 所用时间时是否考虑了对象分配? 这不是使用错误功能的理由。 SO也使用EF。和小巧玲珑。我认为 ADO.NET 也是必要的。 ORM仍然不适合报告导出查询 plain ADO.NET 给了我相同的结果,因此 EF6 本身没有问题。一些本地的东西似乎很疯狂,但我需要知道那里到底出了什么问题。 【参考方案1】:

问题是数据库和链接服务器之间的负载过重。 原生 API 很难通过 SQL 网络接口推送整个记录集。所以代码本身没有问题。

当链接服务器之间的负载较低时,一切都按预期运行。

【讨论】:

【参考方案2】:

我也遇到过同样的问题,当然,SSMS 会执行得更快。

问题在于,所有记录都分配给相应的 POCO,并且它的属性会遍历每个值,直到产生大量对象。

我做了什么来解决这个问题:

我在 sproc 中创建了一个分页(sql 级别的分页)。 除非您是 CYBORG,否则没有人可以一次查看 9000 多条记录。 因此,执行存储过程时只需从结果集中获取 10-100 条记录。

更新:

如果您需要检索结果集以创建 Excel,我会建议可能的方法:

直接从sql创建excel文件,取结果集 从 db 到 C# 非常耗时。 如果您在服务器端仍然需要它 那么我建议你使用EPPlus 或任何其他维护良好的 在服务器端为您生成 excel 的第三方库(我使用的是 EPPlus 并且不包括 SP 执行对我来说不超过 5 秒 时间) 切换回 ado.net 以生成报告是可行的,因为 performance EF 和 ADO 之间存在差异 优化您的查询,细化它。 如果您的双手仍然被客户端束缚,那么请忍受您当前遇到的加载时间;)

【讨论】:

所以分配可能是瓶颈。你是这个意思吗?我知道没有人可以查看它们,但我们的客户同时需要它们。分页意味着我必须为每一页一次又一次地运行计算。每个需要 9-12 秒。不幸的是,无法更改存储过程 @cmxl 是的。分页通常最多增加 1-2 秒,在我的情况下,我几乎没有注意到时间差。其次,尝试说服您的客户进行分页 - 除非客户正在使用结果集生成 Excel 报告。 其实是excel :'( 感谢您的建议!我在我的问题中添加了指向一个非常相似的问题的链接,我可以在 dotTrace 结果中看到该链接。我只是用普通的旧 ADO.NET 尝试过,不幸的是它和 EF6 一样慢。 @cmxl 表示存储过程速度慢,传输过多。至于 Excel,EPPlus 可以从带有sheet.FromDataReader() 的 DbDataReader 生成 Excel 工作表。在开始创建工作表之前,您不必阅读所有内容。

以上是关于在 EF6 中执行 StoredProcedure 需要花费大量时间的主要内容,如果未能解决你的问题,请参考以下文章

在 EF6 中执行复杂的原始 SQL 查询

在 EF6 中,是不是可以使用每个调用结构的上下文跨服务调用执行事务?

EF6中执行Sql语句

EF6 中具有不同执行策略的多个 DbConfiguration

EF6基础系列(12)--- EF进行批量添加/删除

采用ef6连接Oracle 该怎么解决