使用SQL Server 2008 R2网络版。我的桌子有30天的数据。每天包含1200万条记录。每天,我需要导入1200万条新记录并删除1200万条最旧的记录。该表有两个索引,没有键。我正在使用BCP导入新数据。这需要0.5到2.5小时才能运行。我正在批量删除。删除需要20多个小时。删除和重新创建索引似乎没有帮助。什么是最快的解决方案?
col1 varchar(10)
col2 varchar(10)
col3 int
col4 date
index1 (col1, col2, col4)
index2 (col4)
答案 0 :(得分:1)
更新:这不适用于网络版,但由于它在这里,我想我会留在这里。它对其他人有用。
模拟分区的一种方法是在视图中使用union all时创建和截断每日表。
它可能有助于添加几个快速SSD并特别用于日志文件和tempdb。
分区approch
目标是仅通过更新元数据来减少事务日志的数量。
当数据从分区移动或移除时,它会插入和删除行,这将导致事务日志中的(吨)LOB_INSERT_ROWS
和LOB_DELETE_ROW
。
唯一的选择是截断分区,但此选项不存在。
我们可以通过在空分区上使用Merge
和Split
来避免它。
在下面的示例中,我会缩短时间并仅创建过去3个月(即8月,9月和10月)的数据,但您可以轻松地将其扩展到X个月或X天。一旦数据开始添加到11月,8月将被删除,依此类推9月和12月......
文件和文件组
我首先创建6个文件和文件组[Part_0]到[Part_5]:
Alter Database [Test] Add Filegroup [Part_0];
...
Alter Database [Test] Add Filegroup [Part_5];
Alter Database [Test] Add File( NAME = N'Part_0', FILENAME = N'...\Part_0.ndf' , SIZE = 100MB , FILEGROWTH = 100MB ) TO Filegroup [Part_0];
...
Alter Database [Test] Add File( NAME = N'Part_5', FILENAME = N'...\Part_5.ndf' , SIZE = 100MB , FILEGROWTH = 100MB ) TO Filegroup [Part_5];
功能和方案
Create Partition Function [DateKeyPartFunction] (datetime2)
as Range Right For Values ('20150801', '20150901', '20151001', '20151101', '20151201');
Create Partition Scheme [DateKeyPartScheme] as Partition [DateKeyPartFunction]
To ([Part_0], [Part_1], [Part_2], [Part_3], [Part_4], [Part_5]);
同样有6个分区。这将在后面解释,但这主要是因为需要有空分区。
表
因为我不知道你桌子的确切设计,所以我将使用这张桌子:
Create Table dbo.DataPart(id int identity(0, 1), name char(1000), name_date datetime2);
和Clustered Index
:
Create Clustered Index IDX_Part On dbo.DataPart(name_date) On DateKeyPartScheme(name_date);
虚拟数据
此代码在10月至8月(现在)每6秒创建一系列虚拟日期中的超过一百万条记录:
With inc(n) as(
Select ROW_NUMBER() over(order by (select 1))-1 From (
Select 1 From (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as x1(n)
Cross Join (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as x2(n)
Cross Join (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as x3(n)
Cross Join (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as x4(n)
Cross Join (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as x5(n)
Cross Join (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as x6(n)
) as x(n)
)
Insert into dbo.DataPart(name, name_date)
Select TOP(1000000) '', DATEADD(second, -n*6, getdate()) From inc;
分区数据:
这将分配如下:
Id Partition Left Bound Right Bound Row Count
1 [Part_0] < '20150801' 0
2 [Part_1] >= '20150801' < '20150901' 184042
3 [Part_2] >= '20150901' < '20151001' 432000
4 [Part_3] >= '20151001' < '20151101' 383958
5 [Part_4] >= '20151101' < '20151201' 0
6 [Part_5] >= '20151201' 0
十一月
11月一次,新行将转到[Part_4],8月数据可以从[Part_1]中删除。 删除它而不必删除数十万行的唯一方法是将[Part_1]移离表格:
Create Table dbo.DataPart_Temp(id int identity(0, 1), name char(1000), name_date datetime2);
Create Clustered Index IDX_Part_temp On dbo.DataPart_temp(name_date) On [Part_1];
Alter Table DataPart Switch Partition 2 to DataPart_temp Partition 1;
DataPart_Temp
必须与DataPart
(列,索引)相同DataPart
移至DataPart_temp
,因此必须在同一文件组上创建DataPart_temp
上的聚集索引:[Part_1] DataPart
的第二个分区,并切换到DataPart_temp
的第一个也是唯一的分区。所有八月行都在DataPart_temp
。该表现在按以下方式分区:
id Partition Left Bound Right Bound Row Count
1 [Part_0] < '20150801' 0
2 [Part_1] >= '20150801' < '20150901' 0
3 [Part_2] >= '20150901' < '20151001' 432000
4 [Part_3] >= '20151001' < '20151101' 383958
5 [Part_4] >= '20151101' < '20151201' 0
6 [Part_5] >= '20151201' 0
合并
[Part_0]和[Part_1]现在为空,可以合并:
Alter Partition Function [DateKeyPartFunction]() Merge Range ('20150801');
[Part_1]已被删除:
id Partition Left Bound Right Bound Row Count
1 [Part_0] < '20150901' 0
2 [Part_2] >= '20150901' < '20151001' 432000
3 [Part_3] >= '20151001' < '20151101' 383958
4 [Part_4] >= '20151101' < '20151201' 0
5 [Part_5] >= '20151201' 0
下个月添加
现在不再使用[Part_1],它可以作为下一个可用分区添加到分区方案中:
Alter Partition Scheme [DateKeyPartScheme] Next Used [Part_1];
然后[Part_5](12月,&gt; ='20151201')可以被分割:
Alter Partition Function [DateKeyPartFunction]() Split Range ('20160101');
由于[Part_5]为空,因此无需移动任何东西。 [Part_5]的另一半将转到下一个可用分区[Part_1]:
Id Partition Left Bound Right Bound Row Count
1 [Part_0] < '20150901' 0
2 [Part_2] >= '20150901' < '20151001' 432000
3 [Part_3] >= '20151001' < '20151101' 383958
4 [Part_4] >= '20151101' < '20151201' x rows in November
5 [Part_5] >= '20151201' < '20160101' 0
6 [Part_1] >= '20160101' 0
清理
DataPart_temp
现在可以被截断然后删除(或者至少删除它的Clustered Index)。
几个月后
12月,分区ID 2必须移开([Part_2]),与分区1合并,然后在1月拆分之前加回。
为了自动完成此过程,您必须查找分区2的文件组名称:
然后创建动态SQL:
如果使用fn_dblog,您应该看到事务日志很少。
答案 1 :(得分:0)
Web版可能是用于您正在进行的操作类型的错误SKU。这是某种数据仓库解决方案吗?从解决方案视图来看,分区是正确的方法,但是由于这是不可行的,因此一种方法是拥有表的两个副本 - 一个包含当前数据,另一个包含最新数据。如果要直接访问表,请在准备切换时适当地重命名表。如果您通过一些存储过程访问该表,则只需更改过程,使它们指向新表。这一切都可以轻松实现自动化。
当您准备好进行第二天的操作时,您可以开始准备表 - bcp输出您需要的数据,然后重新输入bcp,然后bcp in new data。不需要删除。为什么需要2.5小时的bcp-in - 你有理解为什么吗?我愿意认为这是由于磁盘IO成为瓶颈。那和/或您的数据/日志文件是自动增长的。在准备新表时,您可以通过适当设置恢复模型(批量日志或简单),然后在批量操作完成后将其设置回来来缓解其中的一些。