EF 和表值函数性能

Posted

技术标签:

【中文标题】EF 和表值函数性能【英文标题】:EF and table valued function performance 【发布时间】:2012-09-22 01:10:21 【问题描述】:

我们正在使用 Entity Framework 5.0.0,数据库优先方法。

我已将几个内联表值函数 (ITVF) 导入到我的 EDMX,它们返回 IQueryable。返回后,我们最常对它们进行某种类型的操作,例如 .Skip().Take()

性能很好,至少持续了几个小时。如果不做任何事情(至少我所知道的),性能会在一夜之间下降。

首先,查询将需要大约 500 毫秒来执行。一夜之间,性能下降到查询返回超时异常(30-60 秒)的水平。这只发生在从代码执行表函数时,例如:

IQueryable<MyResult> results =
            _context.MyTableFunction("searchforthis")
                .Skip(currentPagingPosition*20).Take(20);

(这不仅仅是我们第一次运行查询时会发生这种情况)

如果我们直接从 SQL Server Management Studio 运行相同的表函数,查询将在大约 500 毫秒内执行。

如果我像这样“重新保存”表格函数:

ALTER FUNCTION [dbo].[MyTableFunction]......

无需进行任何“真正的”更改,即使从代码中调用,它也会立即恢复到 500 毫秒的速度。

我是否遗漏了一些明显的东西,有什么想法吗?

【问题讨论】:

参数嗅探。 Slow in the Application, Fast in SSMS? Understanding Performance Mysteries 性能问题是否体现在应用程序的 CPU/内存使用率较高? sql runs fast in ssms slow in asp.net的可能重复 @LukeMcGregor - 不,它可以通过单个参数发生。发生这种情况是因为 SQL Server 为传递的第一个参数值编译了一个计划,然后将其缓存并将其重用于所有其他调用。如果该参数不具代表性(例如高度选择性),则所使用的计划对于应用程序传递的其他可能参数值可能是灾难性的。像 OP 一样运行 ALTER FUNCTION 将从缓存中删除坏计划,因此为什么要修复它。 您可以尝试使用OPTIMIZE FOR UNKNOWN。尽管您可能必须通过 plan guide 来执行此操作,因为 EF 正在生成 TSQL。 【参考方案1】:

所以我尝试将 Option Recompile 添加到内联 TVF,但似乎不允许。我发现的唯一方法是创建一个多语句函数,但它们有其缺点:

设置

CREATE Table MyTest
(
    ID INT PRIMARY KEY Identity,
    SomeValue VARCHAR(MAX),
    UpdateDate Date
)
CREATE INDEX IDX_MyTest_UpdateDate
 ON MyTest(UpdateDate)


GO

创建函数

 CREATE FUNCTION MyFunction(@FromDate date, @ToDate Date)
 RETURNS TABLE 
 AS
RETURN
(   
    SELECT SomeValue, UpdateDate
    FROM MyTest
    WHERE   UpdateDate >= @FromDate AND
            UpdateDate <=@ToDate
    -- OPTION (RECOMPILE) **** NOT ALLOWED HERE**
)
GO

CREATE FUNCTION MyFunction2(@FromDate date, @ToDate Date)
 RETURNS @retMyFunction TABLE
    (
        SomeValkue VARCHAR(MAX),
        UpdateDate Date
    ) 
 AS
BEGIN
    INSERT INTO @retMyFunction
    SELECT SomeValue, UpdateDate
    FROM MyTest
    WHERE   UpdateDate >= @FromDate AND
            UpdateDate <=@ToDate
    OPTION (RECOMPILE);

    RETURN;
END
GO

加载数据

 INSERT INTO MyTest (SomeValue, UpdateDate)
 SELECT CAST(X.n * Y.n * z.n AS VARCHAR(Max)), DATEADD(D, ROW_NUMBER() OVER (ORDER BY X.n), '2010-01-01')
 FROM (VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)) X(n)
 CROSS APPLY (VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)) Y(n)
 CROSS APPLY (VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)) Z(n)

测试

-- Index Scan
SELECT * FROM MyTest
 WHERE UpdateDate > '2010-01-02' AND UpdateDate < '2020-01-01'
-- Index Seek
 SELECT * FROM MyTest
 WHERE UpdateDate > '2010-01-02' AND UpdateDate < '2010-01-05'


-- Index Scan
SELECT *
FROM MyFunction('2010-01-02', '2020-01-01')
-- Index Scan
SELECT *
FROM MyFunction('2010-01-02', '2010-01-05')


-- Index Scan
SELECT *
FROM MyFunction2('2010-01-02', '2020-01-01')
-- Index Seek
SELECT *
FROM MyFunction2('2010-01-02', '2010-01-05')

【讨论】:

所以我试着理解你对这个答案的意思,但我不清楚这是否解决了你的问题,你只是说“他们有自己的缺点”。我遇到了一个非常相似的问题,找不到解决方法。 @GabrielEspinoza 我没有问题,我只是在回答问题(不是我的问题!)。多语句函数的缺点是它们逐行执行(很像游标),因此在大型数据集上运行缓慢。恕我直言,最好的选择是编写一个存储过程来返回您的数据集并在 SP 的单个语句或整个 SP 上使用 OPTION RECOMPILE。 如果我告诉你我改变了内联函数而没有任何改变,你会相信吗? - 我猜参数嗅探在优化执行计划失败时真的很痛苦。对它进行一些控制会很棒,比如在 SP 上

以上是关于EF 和表值函数性能的主要内容,如果未能解决你的问题,请参考以下文章

在匹配子句中使用视图和表值函数作为节点或边表

使用带有标量函数的 where 条件与使用交叉应用和表值函数的 where 条件

EF:从 C# 将表值参数传递给用户定义的函数

在 EF6 Code First 中支持表值函数吗?

Entity Framework 4 表值函数

使用实体框架的表值函数