我正在寻找一些改进我的实体框架查询性能的技巧,并且来自this useful article。
本文作者提到了以下内容:
09避免使用视图
视图降低了LINQ查询性能的代价。这些性能很慢并且会大大影响性能。因此,请避免在LINQ to Entities中使用视图。
我在数据库环境中熟悉this meaning of view
。因为我不理解这句话:他的意思是什么?
答案 0 :(得分:3)
在那篇文章中,这是一些非常严肃的微优化 我不会把它当成福音,与EF合作过了很多。 当然,这些事情很重要,但总的来说,它很快。
如果你有一个复杂的视图,然后你在该视图上执行了更多的LINQ,那么肯定的是,它可能会导致一些缓慢的性能,但我不会打赌它。
这篇文章甚至没有任何基准分数!
如果性能是您的程序的一个严重问题,请缩小查询速度并将其发布到此处,请查看SO社区是否可以帮助您优化查询。如果你问我,那么比所有微优化更好的解决方案。
答案 1 :(得分: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
行,总会有至少有一个对应的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()
。 vs (from b in context.B select b.z).Distinct()
。
这两个都应该变成一个非常简单的SELECT DISTINCT z FROM ...
。
这两个应该只变成表B
的表扫描或索引扫描。
第一个可能不是(查询计划中的缺陷),但这将是令人惊讶的。 (快速检查类似的视图确实发现SQLServer忽略了不相关的表)。
第一个可能需要稍长的时间才能生成查询计划,因为必须推断出A.id
上的联接不相关的事实。但是数据库服务器在这方面做得很好;这是一套计算机科学和问题,已经完成了数十年的工作。
如果我不得不打赌,我会打赌这个观点会让事情变得非常缓慢,不过我会更多地认为这是微不足道的,它会消失。用这两种查询进行的实际测试发现两者在差异的相同范围内(即两者的不同时间范围相互重叠)。
在这种情况下,从linq查询生成SQL的效果将是nil(它们在那时实际上是相同的,但名称不同)。
让我们考虑一下我们是否在该视图上有一个触发器,以便插入或删除执行等效的插入或删除。在这里,我们将略微使用一个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视图可以获得巨大的成功。
使用或避免视图的非性能原因包括:
视图的使用会隐藏与该视图相关的实体与代码中与基础表相关的实体之间的关系。这很糟糕,因为你的模型在这方面现在还不完整。
如果视图在除您之外的其他应用程序中使用,您将与其他应用程序更加一致,利用已经经过验证的代码,并自动处理对视图实现的更改。 / p>