SQL Server 2014-内存泄漏和巨大的CPU使用用户定义的函数内的变量

问题描述:

我有一个SQL Server 2014的实例。到主数据库中,我创建了一些用户定义的函数,以便简化使用期间的查询。 NET应用程序。SQL Server 2014-内存泄漏和巨大的CPU使用用户定义的函数内的变量

我在特定函数中遇到了内存和性能问题。这是我正在使用的功能:

ALTER FUNCTION [dbo].[getNotificationInspections](
    @range int = 3 
) 
RETURNS 
@Notifies TABLE 
(
     min_id_history int, 
     max_id_history int, 
     disCounter tinyint, 
     name_job varchar(15), 
     desc_job varchar(150), 
     id_inspect smallint, 
     pc_host varchar(15), 
     name_inspect varchar(10), 
     link_image varchar(100) 
) 
AS 
BEGIN 

    DECLARE @minHistory int 
    DECLARE @maxHistory int 

    SELECT @minHistory = minHistory, 
    @maxHistory = maxHistory 
    FROM getLastEngineRange(@range) 

    --SELECT @minHistory, @macHistory 

    INSERT @Notifies 
    SELECT @minHistory min_id_history, @macHistory max_id_history, DS.* 
     FROM dbo.RealDiscardRange(@minHistory, @maxHistory) DS 
     WHERE DS.disCounter = @range 

    RETURN 
END 

此功能工作正常,除了它需要约4-5秒和约1.5Gb的内存! 3排!但让我们继续看看发生了什么。

如果我执行定义@maxHistory唯一的查询和@minHistory它不使用CPU或时间或内存。

如果我使用“使用的值,而不是变量”的INSERT @Notifies查询它花费200ms左右,无记忆!

因此,在查询中使用变量需要巨大的CPU和内存!

事实上,如果我用这个查询出来的功能,但作为一个简单的查询:

INSERT @Notifies 
    EXEC('SELECT min_id_history=''' + @minHistory + ''', 
     max_id_history=''' + @maxHistory + ''', DS.* 
     FROM dbo.RealDiscardRange(' + @minHistory + ', ' + @maxHistory + ') DS 
     WHERE DS.disCounter = ' + @range + ' ') 

    RETURN 

的时间和内存spended执行这个人是可以忽略不计。每次SQL Server服务时都会重新启动这些测试。

我希望有人能解释这一点。

+0

我认为,它与你的问题中的错字没有关系?你在返回的SELECT语句中有macHistory而不是maxHistory? –

+2

最有可能是次优执行计划而不是内存泄漏。 SQL Server缓存数据,直到检测到内存压力时才释放内存。不理想的计划会触及更多的数据,并因此使用更多缓存。 –

+1

你如何测量内存消耗?请发布执行计划。 –

[1]我并不是说SQL Server不具备这样的错误(如某处内存泄漏),但99.999%,我觉得这是不是这样的。给出我没有其他信息的事实,对于某些运营商从执行计划中生成的行数,它似乎只是一个错误的估计(高估)。

上述[2]的源代码不编译的:不是

SELECT ..., @macHistory max_id_history, ... 

应该是

SELECT ..., @maxHistory max_id_history, ... 

[3]代替多步TVF的我将因此测试一个内联函数:

ALTER FUNCTION [dbo].[getNotificationInspections](
    @range int = 3 
) 
RETURNS TABLE 
AS 
RETURN 
SELECT min_id_history = f1.minHistory, 
     max_id_history = f1.maxHistory, 
     disCounter  = ds.disCounter, 
     name_job  = ds.name_job, 
     desc_job  = ds.desc_job, 
     id_inspect  = ds.id_inspect, 
     pc_host   = ds.pc_host, 
     name_inspect = ds.name_inspect, 
     link_image  = ds.link_image 
FROM dbo.getLastEngineRange(@range) f1 
CROSS APPLY dbo.RealDiscardRange(f1.minHistory, f1.maxHistory) ds 
WHERE ds.disCounter = @range; 
GO 

原因是,大多数时候,内联TVFs执行多步相比,因为TVFs多级较好查询优化器就像一个黑盒子。相反,内联TVF只不过是一个带参数的视图,对于每个内联TVF调用,SQL Server将源代码扩展到主查询(调用者)源代码中,然后优化所有源代码(作为all)。

[4]如果没有帮助,那么你应该公布执行计划(也许这是更好地在任何情况下,发布此XP)。