2015-11-04 59 views
0

使用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

我正在使用網絡版,我不認爲支持分區。 –

+0

我會看看你的[內存,CPU和磁盤開銷](https://msdn.microsoft.com/en-us/library/ms190994(v = sql.105).aspx)。 –

回答

1

更新:這會不會與網絡版的工作,但因爲它是在這裏,我想我會離開這裏。它對其他人可能有用。

模擬分區的一種方法是在視圖中使用union all時創建和截斷每日表格。

這可能會有助於添加幾個快速SSD並將其用於日誌文件和tempdb。

分區計算策略

的目標是通過僅更新元數據,以減少事務日誌的數量。

當數據移動或從分區中移除時,它將插入和刪除行,這將導致事務日誌中的(噸)LOB_INSERT_ROWSLOB_DELETE_ROW

唯一的選擇是截斷分區,但該選項不存在。 我們可以通過在空分區上僅使用MergeSplit來避免它。

在下面的示例中,我會將事情縮短並且只創建過去3個月(即8月,9月和10月)的數據,但您可以輕鬆地將其擴展到X個月或X天。一旦數據開始被添加到十一月等都與九月和十二月八月將被刪除...

文件和文件組

我第一次創建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); 

虛擬數據

此代碼在一系列假日期產生上百萬的記錄,從十月每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 
  • [Part_1],[Part_2],[Part_3]包含8月,9月和10月的數據。
  • [Part_0](即<'20150801')僅存在,因爲在我們可以在11月份清空[Part_1](8月)之前需要一個空分區。這將是7月份和之前的數據,但必須保持空白。
  • 它使事情變得更容易,下個月已經有一個空的分區,雖然它將保持爲空,直到它到達十一月。這是11月份[Part_4]的目的。
  • 分區方案需要額外的分區來處理右邊界以外的所有事情。這是12月及以後的[Part_5],它也必須保持空白。

一旦進入十一月,新行會去[Part_4]八月數據可以從[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(列,索引)
  • 因爲[Part_1]是從移動到DataPartDataPart_temp,上DataPart_temp聚集索引必須在相同的文件組被創建:[Part_1]
  • [Part_1]是DataPart第二分區,並切換到第一和唯一的分區ö f 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月,> ='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現在可以截斷,然後丟棄(或者至少降其聚集指數)。

接下來的幾個月

十二月,分區ID 2必須移開([Part_2]),合併分區1,然後一月分裂之前加回。

爲了自動化這個過程中,你將不得不尋找文件組的名稱爲分區2:

然後創建動態SQL:

  • 添加聚集索引DataPart_temp上[ Part_X](類似於DataPart)
  • 開關[Part_X]從DataPart到DataPart_temp
  • 合併分區1和2
  • 添加[Part_X]至DataPart
  • 拆分最後一個分區
  • 截斷DataPart_temp
  • 降DataPart_temp或其聚集索引

如果使用fn_dblog,你應該看到,事務日誌是最小的。

0

網絡版可能是您正在使用的操作類型所使用的錯誤SKU。這是一種數據倉庫解決方案嗎?從解決方案的角度來看,分區是正確的方法,但是由於這是不可行的,一種方法是擁有兩個表格副本 - 一個具有當前數據,另一個具有最新數據。如果您直接訪問表格,則在準備好切換時適當重命名錶格。如果你通過一些存儲過程訪問表,那麼只需改變過程,使它們指向新表。這可以很容易實現自動化。

當您準備好接受第二天的操作時,您可以開始準備表 - bcp取出您需要的數據,然後重新輸入數據,然後使用bcp輸入新數據。不需要刪除。爲什麼需要長達2.5個小時才能完成 - 你有理解爲什麼?我敢打賭,這是由於磁盤IO成爲瓶頸。那個和/或你的數據/日誌文件是自動生長的。在準備新表時,可以通過適當地設置恢復模式(批量日誌或簡單),然後在批量操作完成後將其設置回來,從而減輕其中的一部分。