【问题标题】:Do Views degrade the LINQ query performance?视图会降低 LINQ 查询性能吗?
【发布时间】:2014-02-15 02:54:18
【问题描述】:

我正在寻找一些技巧来提高我的实体框架查询性能,结果遇到了this useful article

本文作者提到以下几点:

09 避免使用视图

视图会降低 LINQ 查询性能,代价高昂。这些在性能上很慢并且对性能影响很大。所以避免在 LINQ to Entities 中使用视图。

我只是熟悉数据库上下文中的this meaning of view。而且因为我不明白这句话:他指的是哪些观点?

【问题讨论】:

  • 恕我直言,EF 本身比视图引入的任何东西更有可能通过糟糕的 sql 和延迟加载引入性能问题!
  • 这是可能的,但您必须调查 Linq 实际处理视图的方式,即便如此,它也可能取决于视图/查询的复杂性。我已经看到其他 ORM 在使用视图时由于它们处理视图的方式而显着降低了性能,但我自己还没有亲自调查过与 Linq/EF 相关的视图。

标签: c# sql performance linq entity-framework


【解决方案1】:

那篇文章中有一些非常严肃的微优化。
我个人不会把它当作福音,我曾与 EF 合作过很多次。 当然这些事情很重要,但一般来说,它很快。

如果您有一个复杂的视图,然后您正在对该视图执行进一步的 LINQ,那么当然,它可能会导致一些缓慢的性能,但我不会打赌。

这篇文章甚至没有任何基准!

如果性能对您的程序来说是一个严重问题,请缩小查询速度较慢的范围并将其发布在此处,看看 SO 社区是否可以帮助您优化查询。如果你问我,比所有微优化更好的解决方案。

【讨论】:

  • 但是访问从视图中检索数据与从表中检索数据不完全相同吗?查询是否快速不是数据库的问题吗?我正在使用实体框架来访问具有数十亿数据集的视图。我有一个伟大而快速的数据库。所以我只是想知道为什么一张桌子会更快。
  • 如果您有一个包含数十亿条目的视图,那么无论您做什么,它都不会很快。一个视图实际上仍然是一个查询,尽管在多个表上,所以是的,一个表的查询与一个视图的查询可能会更快,但是如果您需要将所有数据组合到一个视图中,那么您要么使用 LINQ(可能很混乱)要么查询视图,而不是其他任何选项。
  • 是的,我同意。这听起来合乎逻辑。
  • 视图不是查询,它是查询的一部分的描述。要么你需要视图所做的所有工作——因此差异可以忽略不计——或者冗余将被查询计划器删除——因此可以忽略不计。在某些情况下,使用视图对性能产生的影响与您可能拥有多少亿行无关。
【解决方案2】:

这取决于,但在很大程度上很少见。

假设我们有这样的视图:

CREATE VIEW TestView
AS
Select A.x, B.y, B.z
FROM A JOIN B on A.id = B.id

我们为此创建了一个实体映射。

我们还假设 B.id 已绑定,因此它不可为空,并且与 A.id 具有外键关系 - 也就是说,只要有 B 行,总是至少有一个对应的 @987654325 @。

现在,如果我们可以用from t in context.TestView where t.x == 3 代替from a in context.A join b in context.B on a.id equals b.id where a.x == 3 select new {a.x, b.y, b.z}

我们可以预期前者转换为 SQL 的速度会稍微快一些,因为它是一个稍微简单的查询(从 Linq 和 SQL 的角度来看)。

我们可以预期后者从 SQL 查询转换为 SQLServer(或其他)内部查询的速度会稍微快一些。

我们可以预期内部查询几乎相同,除非有些奇怪。因此,我们预计此时的性能是相同的。

总而言之,它们之间没有太多选择。如果我必须赌一个,我会用视图稍微快一点的情况下赌注,尤其是在第一次跟注时,但我不会下很多赌注。

现在让我们考虑(from t in context.TestView select t.z).Distinct()。与(from b in context.B select b.z).Distinct().

这两个都应该变成一个非常简单的SELECT DISTINCT z FROM ...

这两者都应该变成只对表B进行表扫描或索引扫描。

第一个可能不是(查询计划中的缺陷),但这会令人惊讶。 (对类似视图的快速检查确实发现 SQLServer 忽略了不相关的表)。

第一个可能需要稍长的时间来生成查询计划,因为必须推断出 A.id 上的连接是无关紧要的事实。但是数据库服务器擅长这种事情;这是一组计算机科学和问题,已经完成了数十年的工作。

如果我必须押注其中一个,我会押注让事情变得稍微慢一些的观点,尽管我更押注它是如此微小的差异以至于它消失了。对这两种查询的实际测试发现两者在相同的差异范围内(即两者的不同时间范围相互重叠)。

在这种情况下,从 linq 查询生成 SQL 的效果将为零(此时它们实际上是相同的,但名称不同)。

让我们考虑一下我们是否在该视图上有一个触发器,以便插入或删除执行等效的插入或删除。在这里,我们将从使用一个 SQL 查询而不是两个(或更多)查询中获得一些好处,并且更容易确保它发生在单个事务中。因此,在这种情况下,浏览量略有增加。

现在,让我们考虑一个更复杂的视图:

CREATE VIEW Complicated
AS
Select A.x, B.x as y, C.z, COALESCE(D.f, D.g, E.h) as foo
FROM
A JOIN B on A.r = B.f + 2
JOIN C on COALESCE(A.g, B.x) = C.x
JOIN D on D.flag | C.flagMask <> 0
WHERE EXISTS (SELECT null from G where G.x + G.y = A.bar AND G.deleted = 0)
AND A.deleted = 0 AND B.deleted = 0

我们可以在 linq 级别完成所有这些工作。如果我们这样做了,随着查询生产的进行,它可能会有点昂贵,尽管这很少是 linq 查询整体命中中最昂贵的部分,尽管编译查询可能会平衡这一点。

我倾向于将视图作为更有效的方法,但如果这是我使用该视图的唯一原因,我会介绍一下。

现在让我们考虑:

CREATE VIEW AllAncestry
AS
WITH recurseAncetry (ancestorID, descendantID)
AS
(
    SELECT parentID, childID
    FROM Parentage
    WHERE parentID IS NOT NULL

    UNION ALL

    SELECT ancestorID, childID
    FROM recurseAncetry
        INNER JOIN Parentage ON parentID = descendantID
)
SELECT DISTINCT (cast(ancestorID as bigint) * 0x100000000 + descendantID) as id, ancestorID, descendantID
FROM recurseAncetry

从概念上讲,此视图执行大量选择;进行选择,然后根据该选择的结果递归地进行选择,依此类推,直到它具有所有可能的结果。

在实际执行中,这会转化为两次表扫描和一次惰性假​​脱机。

基于 linq 的等价物会更重;真的,你最好要么调用等效的原始 SQL,要么将表加载到内存中,然后在 C# 中生成完整的图表(但请注意,这将浪费基于不需要的查询一切)。

总而言之,在这里使用视图会节省很多。

总结;使用视图通常对性能的影响可以忽略不计,而且这种影响可以是任何一种方式。使用带有触发器的视图可以稍微提高性能,并通过强制数据在单个事务中发生更容易确保数据完整性。使用带有 CTE 的视图可以大大提高性能。

使用或避免使用视图的非性能原因是:

  1. 视图的使用在代码中隐藏了与该视图相关的实体和与基础表相关的实体之间的关系。这很糟糕,因为您的模型现在在这方面不完整。

  2. 1234563 /p>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-25
    • 1970-01-01
    • 2018-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-26
    • 1970-01-01
    相关资源
    最近更新 更多