【发布时间】:2011-11-29 09:57:55
【问题描述】:
我有一个包含一些买卖数据的表,其中包含大约 800 万条记录:
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。我已经创建了这样的存储过程,它采用表值参数:
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]
【问题讨论】:
-
“大约 7 秒,表格有 70k 条记录” - 听起来不是特别快。你有什么索引?什么样的 I/O 子系统?
-
@MitchWheat 我用索引更新了问题。 I/O 子系统 - 如果您要求的话,有 2 个 ssd 驱动器 raid。我每晚都重建索引。
-
是的。数据库服务器是 SQL Server 2008 R2。数据库以“简单”恢复模式运行。数据文件8.1GB,日志文件133MB
-
哪个表有 70K 行?电视节目主持人?如果不是 TVP 中通常有多少行?另外你说哪个
when not matched是瓶颈?有两个.. -
@MartinSmith 我写的数字是关于事务表的。 DELETE/INSERT 我在有 8M 记录的表上进行了测试。 MERGE 我在最多 180k 条记录的表上进行了测试,结果非常糟糕,以至于我停止了。 TVP 大多是 1-10 条记录。很少有多达 100 条记录。
标签: sql-server tsql merge sql-delete