我正在寻找一些提示,以提高我的实体框架查询性能,并出现了this useful article。Do Views是否会降低LINQ查询性能?
这篇文章的作者提到以下几点:
09避免使用浏览
次数降低LINQ查询性能昂贵。这些性能很慢,对性能影响很大。所以避免在LINQ to Entities中使用视图。
我刚刚在数据库环境中熟悉this meaning of view
。而且我不明白这个说法:他是指什么意见?
我正在寻找一些提示,以提高我的实体框架查询性能,并出现了this useful article。Do Views是否会降低LINQ查询性能?
这篇文章的作者提到以下几点:
09避免使用浏览
次数降低LINQ查询性能昂贵。这些性能很慢,对性能影响很大。所以避免在LINQ to Entities中使用视图。
我刚刚在数据库环境中熟悉this meaning of view
。而且我不明白这个说法:他是指什么意见?
这是文章中的一些非常严重的微优化。
我个人不会把它当作福音,与EF合作过很久。 确定这些事情可能很重要,但总的来说,这很快。
如果你有一个复杂的观点,然后你对这个视图进行了进一步的LINQ,那么肯定的是,它可能会导致一些慢的性能,尽管如此我也不会下注。
该文章甚至没有任何基准标记!
如果性能对您的程序来说是一个严重问题,请缩小哪些查询速度慢并在此处发布,请查看SO社区是否可以帮助优化查询。如果你问我,那么比所有微型优化更好的解决方案。
这取决于,但很少达到显着程度。
比方说,我们已经像一个观点:
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
行,总有至少一个对应A
。
现在,如果我们可以做一些像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一起使用观点可能是一个巨大的成就。
使用或避免的看法虽然不履行的原因是:
使用视图隐藏与该视图的实体和关系到你的代码中基础表的实体之间的关系。这很糟糕,因为您的模型在这方面现在不完整。
如果这些视图在您的其他应用程序中使用,那么您将与其他应用程序更加一致,利用已经经过验证的代码,并自动处理对视图实现的更改。
恕我直言,EF本身更可能通过错误的sql和延迟加载来引入性能问题,而不是任何视图将会引入的问题! – Liath
这是可能的,但是你必须研究Linq实际处理视图的方式,甚至可以根据视图/查询的复杂性来决定。我已经看到其他ORM在使用视图时会受到显着的性能影响,这是因为他们处理视图的方式,但我本人尚未亲自调查与Linq/EF有关的视图。 –