Azure-Sql-Database

如何修復特定查詢的高 DTU 使用率

  • October 15, 2020

我有一個在 Microsoft SQL Azure 數據庫上每隔幾秒鐘執行一次的查詢。它來自一個 Web 作業,該作業檢查是否需要根據特定表(它的排隊表)中的行執行操作。根據所附圖片,是否有人對我如何減少相關查詢的 DTU 使用率有任何建議,它導致數據庫達到 100% CPU。

在此處輸入圖像描述

沒有完整的表定義,我將嘗試一下它可能是什麼。根據您問題中的螢幕截圖,表格可能如下所示:

CREATE TABLE dbo.TaskSchedulerItem (
   TaskSchedulerItemID int IDENTITY(1,1),
   TaskSchedulerID int,
   ItemStatus varchar(10),
   DateLastUpdated datetime2(0),
   CONSTRAINT PK_TaskSchedulerItem PRIMARY KEY CLUSTERED (TaskSchedulerItemID)
);

只是為了好玩,讓我們在其中放入一些隨機樣本數據:

INSERT INTO dbo.TaskSchedulerItem (ItemStatus, TaskSchedulerID, DateLastUpdated)
SELECT TOP 10 'Pending', column_id, GETDATE()
FROM sys.columns
UNION ALL
SELECT TOP 10000 'Complete', column_id, GETDATE()-1
FROM sys.columns;

現在我們可以針對該數據執行查詢:

DECLARE @CurrentDateTime datetime2(0)   = GETDATE(),
       @TaskSchedulerID int            = 4,
       @TaskSchedulerItemID int;

SELECT TOP 1 @TaskSchedulerItemID = TaskSchedulerItemID
FROM dbo.TaskSchedulerItem
WHERE (
   ItemStatus IN ('Pending','Failed')
   OR
   ItemStatus = 'IN PROGRESS' AND DateLastUpdated < DATEADD(MINUTE, -1, @CurrentDateTime)
)
AND TaskSchedulerID = @TaskSchedulerID
ORDER BY DateLastUpdated;

SELECT @TaskSchedulerItemID;

我們可以看到這個查詢執行了聚集索引掃描聚集索引掃描

我不確定您的查詢是否正在執行聚集索引掃描,但這將是我的猜測。我們只是在尋找一行——理想情況下,我們將能夠對這一行(或者,至少更少的行)進行搜尋。ed 標準使這OR有點棘手,因此擁有一個始終為我們提供確切行的單一索引有點具有挑戰性。相反,我們可以嘗試接近

我們可以創建一個索引TaskSchedulerID(我們總是過濾那個!),DateLastUpdated(我們總是排序!),和ItemStatus(我們過濾那個,但是在 OR 中,所以 SQL 搜尋它有點棘手):

CREATE INDEX ix_TaskSchedulerId_ItemStatus 
   ON dbo.TaskSchedulerItem(TaskSchedulerID, DateLastUpdated, ItemStatus);

噓!它在尋找!

索引搜尋

這應該使查詢非常快……但是那個小警告符號是什麼? 尋求警告

這就是我所說的“接近”部分。在我的隨機抽樣中,索引搜尋能夠搜尋到 280 行,然後執行一些剩餘 I/O 來找到它需要的符合標準的一行。根據我創建的索引,它將尋找提供的@TaskSchedulerID,然後開始沿著DateLastUpdated列執行(請記住,這是ORDER BY查詢中的 ,然後執行“剩餘 I/O”以找出該ItemStatus條件。

根據您的數據,這可能足以解決您的性能問題。或者你可能需要更聰明一點。由於我不知道您的實際架構、索引或數據分佈,因此我無法做出更好的猜測。

但實際上,您只需要一個可以使您的查詢執行得更快的索引,這樣您就可以使用更少的 DTU。

您提到它是一個隊列表,因此數據分佈可能是傾斜的:大多數行具有表示完成的狀態,理想情況下只有少數行將成為處理的候選者。對於圖像中的查詢,謂詞中使用的 ItemStatus 值是文字,因此這是過濾索引的完美案例:

CREATE INDEX index_name
ON dbo.TaskSchedulerItem(TaskSchedulerID)
INCLUDE(DateLastUpdated, ItemStatus, TaskSchedulerItemId)
WHERE
   ItemStatus IN ('PENDING','FAILED', 'IN PROGRESS');

此外,定期清除表會有所幫助:如果它確實是一個隊列,則無需保留已處理的行。

引用自:https://dba.stackexchange.com/questions/250789