2017-07-02 37 views
-2

我有一個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

你如何測量內存消耗?請發佈執行計劃。 –

回答

2

[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)。