我的表中包含一些买入/卖出数据,其中包含约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
此过程大约需要7秒,表格中有70k记录。所以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]
答案 0 :(得分:5)
好的,这也是另一种方法。对于类似的问题(大型扫描,如果没有匹配源然后删除)我将MERGE执行时间从806ms减少到6ms!
上述问题的一个问题是“WHEN NOT MATCHED BY SOURCE”子句正在扫描整个TARGET表。
这并不是那么明显,但微软允许在进行合并之前过滤(通过使用CTE)TARGET表。所以在我的情况下,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;
答案 1 :(得分:2)
对IsDeleted(或许多人这样做的IsActive)使用BIT字段是有效的,但它确实需要修改所有代码并创建一个单独的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的查询产生负面影响,这就是你需要彻底测试的原因。最后,您需要针对最常见和/或昂贵的查询调整系统,并且可能不得不接受某些查询会因此而变慢,但这可能比此操作更快。
答案 2 :(得分:0)
请参阅此https://stackoverflow.com/questions/3685141/how-to-....
更新是否与删除成本相同?不会。更新将是 一个更轻松的操作,特别是如果你有PK的索引 (错误,这是一个guid,而不是int)。重点是更新 比特场要便宜得多。 (大规模)删除会强制a 重新洗牌数据。
根据这些信息,您使用位字段的想法非常有效。