【发布时间】:2014-03-27 16:11:50
【问题描述】:
我有一个数据库表,它以每台/秒/设备约 4 条记录的速度收集记录。这张桌子很快就变大了。当一个设备完成其任务时,另一个进程将遍历所有记录,执行一些操作,将它们组合成 5 分钟的块,压缩它们并存储它们以供以后使用。然后它会删除该表中该设备的所有记录。
目前有几台设备的近 100 万条记录。我可以很好地循环它们以执行处理,它出现了,但是当我尝试删除它们时,我超时了。有没有办法更快地删除这些记录?也许通过暂时关闭对象跟踪?使用一些锁定提示?当每个设备开始其任务时简单地为每个设备创建一个单独的表,然后在数据处理完成后将其删除,这种设计会更好吗?超时设置为 10 分钟。如果可能的话,我真的很想在 10 分钟内完成这个过程。
CREATE TABLE [dbo].[case_waveform_data] (
[case_id] INT NOT NULL,
[channel_index] INT NOT NULL,
[seconds_between_points] REAL NOT NULL,
[last_time_stamp] DATETIME NOT NULL,
[value_array] VARBINARY (8000) NULL,
[first_time_stamp] DATETIME NULL
);
CREATE CLUSTERED INDEX [ClusteredIndex-caseis-channelindex] ON [dbo]. [case_waveform_data]
(
[case_id] ASC,
[channel_index] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
CREATE NONCLUSTERED INDEX [NonClusteredIndex-all-fields] ON [dbo].[case_waveform_data]
(
[case_id] ASC,
[channel_index] ASC,
[last_time_stamp] ASC
)
INCLUDE ( [seconds_between_points],
[value_array]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON PRIMARY]
SQL Server 2008+ 标准是数据库平台
2014 年 3 月 31 日更新:
我已经开始走上一条似乎有问题的道路。这真的有那么糟糕吗? 我正在创建一个存储过程,它接受一个包含我要附加的数据的表值参数和一个包含设备唯一表名的 varchar 参数。此存储过程将检查表是否存在,如果不存在,则使用特定结构创建它。然后它将插入来自 TVP 的数据。我看到的问题是我必须在 SP 中使用动态 sql,因为似乎无法将表名作为变量传递给 CREATE 或 INSERT。另外,我读到的每一篇关于如何做到这一点的文章都说不要......
不幸的是,如果我有一个表以 4/秒/设备的频率获取所有插入,那么即使在 case_id 和 channel_index 上使用聚集索引,仅对特定 case_id 的表进行计数也需要 17 分钟.因此,尝试删除它们大约需要 25 - 30 分钟。这也会导致发生锁定,因此插入开始花费的时间越来越长,从而导致服务落后。这甚至在没有删除的情况下也会发生。 所描述的存储过程旨在将插入从 4/sec/device 减少到 1/sec/device,并且可以在完成后删除表,而不是单独删除每条记录。想法?
2014 年 3 月 31 日更新 2
我没有按照您的想法使用游标或任何循环。这是我用来循环记录的代码。但是,它以可接受的速度运行:
using (SqlConnection liveconn = new SqlConnection(LiveORDataManager.ConnectionString))
{
using (SqlCommand command = liveconn.CreateCommand())
{
command.CommandText = channelQueryString;
command.Parameters.AddWithValue("channelIndex", channel);
command.CommandTimeout = 600;
liveconn.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
var item = new
{
//case_id = reader.GetInt32(0),
channel_index = reader.GetInt32(0),
last_time_stamp = reader.GetDateTime(1),
seconds_between_points = reader.GetFloat(2),
value_array = (byte[])reader.GetSqlBinary(3)
};
// Perform processing on item
}
}
}
我用来删除的SQL很琐碎:
DELETE FROM case_waveform_data where case_id = @CaseId
这一行需要 25+ 分钟才能删除 100 万行
样本数据(value_array 被截断):
case_id channel_index seconds_between_points last_time_stamp value_array first_time_stamp
7823 0 0.002 2014-03-31 15:00:40.660 0x1F8B0800000000000400636060 NULL
7823 0 0.002 2014-03-31 15:00:41.673 0x1F8B08000000000004006360646060F80F04201A04F8418C3F4082DBFBA2F29E5 NULL
7823 0 0.002 2014-03-31 15:00:42.690 0x1F8B08000000000004006360646060F80F04201A04F8418C3F4082DBFB NULL
【问题讨论】:
-
根据 first_time_stamp 删除超过 x 分钟前的所有内容的计划作业怎么样?
-
用位标志来标记要删除的记录,然后在系统安静时再做?
-
企业版还是标准版?如果是 Enterprise,听起来像是分区切换的工作......
-
如果可能的话,我不希望在夜间作业中一次删除所有数据。另外,我不知道每次安装的安静时间是什么时候。数据只能在案例完成后删除,因此我通过 case_id 将其删除。案例可以持续不到一个小时或长达 60 天。
-
"...循环遍历所有记录..."、"...分别删除每条记录..." 这些是暗示游标或循环而不是基于集合的处理的可怕描述。数百万行通常不应该是一个问题,您的动态分区方案可能不是最好的方法。我认为您应该发布插入/更新/删除的 SQL、此表上的任何键/约束/索引,以及可能的一些示例行。
标签: performance sql-server-2008