簡體   English   中英

Sql Server刪除和合並性能

[英]Sql Server Delete and Merge performance

我的表中包含一些買入/賣出數據,其中包含大約8M的記錄:

CREATE TABLE [dbo].[Transactions](
[id] [int] IDENTITY(1,1) NOT NULL,
[itemId] [bigint] NOT NULL,
[dt] [datetime] NOT NULL,
[count] [int] NOT NULL,
[price] [float] NOT NULL,
[platform] [char](1) NOT NULL
) ON [PRIMARY]

每個X分鍾我的程序為每個itemId獲取新的事務,我需要更新它。 我的第一個解決方案是兩步DELETE + INSERT:

delete from Transactions where platform=@platform and itemid=@itemid
insert into Transactions (platform,itemid,dt,count,price) values (@platform,@itemid,@dt,@count,@price)
[...]
insert into Transactions (platform,itemid,dt,count,price) values (@platform,@itemid,@dt,@count,@price)

問題是,這個DELETE語句平均需要5秒。 這太長了。

我找到的第二個解決方案是使用MERGE。 我創建了這樣的存儲過程,wchich采用表值參數:

CREATE PROCEDURE [dbo].[sp_updateTransactions]
@Table dbo.tp_Transactions readonly,
@itemId bigint,
@platform char(1)
AS
BEGIN
MERGE Transactions AS TARGET
USING @Table AS SOURCE  
ON (    
TARGET.[itemId] = SOURCE.[itemId] AND
TARGET.[platform] = SOURCE.[platform] AND 
TARGET.[dt] = SOURCE.[dt] AND 
TARGET.[count] = SOURCE.[count] AND
TARGET.[price] = SOURCE.[price] ) 


WHEN NOT MATCHED BY TARGET THEN 
INSERT VALUES (SOURCE.[itemId], 
                SOURCE.[dt],
                SOURCE.[count],
                SOURCE.[price],
                SOURCE.[platform])

WHEN NOT MATCHED BY SOURCE AND TARGET.[itemId] = @itemId AND TARGET.[platform] = @platform THEN 
DELETE;

END

對於具有70k記錄的表,此過程大約需要7秒。 所以8M可能需要幾分鍾。 瓶頸是“當不匹配”時 - 當我評論此行時,此過程平均運行0,01秒。

所以問題是:如何提高刪除語句的性能?

需要刪除才能確保該表不包含已在應用程序中刪除的事務。 但真正的情況是它很少發生,在10000次交易更新中,刪除記錄的真正需求小於1。

我的理論解決方法是創建額外的列,如“transactionDeleted bit”並使用UPDATE而不是DELETE,然后每隔X分鍾或幾小時通過批處理作業清理表並執行

delete from transactions where transactionDeleted=1

它應該更快,但我需要更新應用程序的其他部分中的所有SELECT語句,僅使用transactionDeleted = 0記錄,因此它也可能會影響應用程序性能。

你知道更好的解決方案嗎?

更新:當前索引:

CREATE NONCLUSTERED INDEX [IX1] ON [dbo].[Transactions] 
(
[platform] ASC,
[ItemId] ASC
) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF,   IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 50) ON [PRIMARY]


CONSTRAINT [IX2] UNIQUE NONCLUSTERED 
(
[ItemId] DESC,
[count] ASC,
[dt] DESC,
[platform] ASC,
[price] ASC
) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

好的,這也是另一種方法。 對於類似的問題(大掃描當沒有匹配源然后刪除)我將MERGE執行時間從806ms減少到6ms!

上述問題的一個問題是“WHEN NOT MATCHED BY SOURCE”子句正在掃描整個TARGET表。

這並不是那么明顯,但Microsoft允許在進行合並之前過濾TARGET表(通過使用CTE)。 所以在我的情況下,TARGET行從250K減少到不到10行。 巨大差距。

假設上述問題適用於由@itemid和@platform過濾的TARGET,那么MERGE代碼將如下所示。 上面對索引的更改也有助於這個邏輯。

WITH Transactions_CTE (itemId
                        ,dt
                        ,count
                        ,price
                        ,platform
                        )
AS
-- Define the CTE query that will reduce the size of the TARGET table.  
(  
    SELECT itemId
        ,dt
        ,count
        ,price
        ,platform
    FROM Transactions  
    WHERE itemId = @itemId
      AND platform = @platform  
)  
MERGE Transactions_CTE AS TARGET
USING @Table AS SOURCE
    ON (
        TARGET.[itemId] = SOURCE.[itemId]
        AND TARGET.[platform] = SOURCE.[platform]
        AND TARGET.[dt] = SOURCE.[dt]
        AND TARGET.[count] = SOURCE.[count]
        AND TARGET.[price] = SOURCE.[price]
        )
WHEN NOT MATCHED BY TARGET  THEN
        INSERT
        VALUES (
            SOURCE.[itemId]
            ,SOURCE.[dt]
            ,SOURCE.[count]
            ,SOURCE.[price]
            ,SOURCE.[platform]
            )
WHEN NOT MATCHED BY SOURCE THEN
        DELETE;

使用IsDeleted的BIT字段(或許多人所做的IsActive)是有效的,但它確實需要修改所有代碼,並且需要創建一個單獨的SQL作業來定期通過並刪除“已刪除”的記錄。 這可能是要走的路,但首先嘗試的東西不那么干擾。

我在你的2個索引中注意到它們都不是CLUSTERED。 我可以假設IDENTITY字段是? 您可以考慮將[IX2] UNIQUE索引設置為CLUSTERED並更改PK(再次,我假設IDENTITY字段是CLUSTERED PK)為NONCLUSTERED。 我還會重新排序IX2字段以將[Platform]和[ItemID]放在第一位。 由於您的主要操作是將[Platform]和[ItemID]作為一個集合進行查找,因此以這種方式對它們進行物理排序可能會有所幫助。 由於這個索引是獨一無二的,因此它是CLUSTERED的一個很好的候選者。 它肯定值得測試,因為這會影響對表的所有查詢。

此外,如果按照我的建議更改索引有幫助,那么仍然可能值得嘗試這兩個想法,因此也要進行IsDeleted字段以查看是否會進一步提高性能。

編輯:我忘了提到,通過使IX2索引CLUSTERED並將[Platform]字段移到頂部,你應該擺脫IX1索引。

EDIT2:

只是要非常清楚,我建議像:

CREATE UNIQUE CLUSTERED  INDEX [IX2]
(
[ItemId] DESC,
[platform] ASC,
[count] ASC,
[dt] DESC,
[price] ASC
)

公平地說,更改哪個索引是CLUSTERED也會對在[id]字段上完成JOIN的查詢產生負面影響,這就是您需要徹底測試的原因。 最后,您需要針對最頻繁和/或最昂貴的查詢調整系統,並且可能必須接受某些查詢會因此而變慢,但這種操作可能更快。

請參閱https://stackoverflow.com/questions/3685141/how-to-....

更新是否與刪除成本相同? 不會。更新操作會更輕松,特別是如果你有PK的索引(errrr,那是一個guid,而不是int)。 關鍵是對位字段的更新要便宜得多。 (大量)刪除將迫使數據重新洗牌。

根據這些信息,您使用位字段的想法非常有效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM