PK 索引中列的順序是否重要?
我有幾張非常大的桌子,基本結構相同。每個都有一個
RowNumber (bigint)
和DataDate (date)
列。每晚使用 SQLBulkImport 載入數據,並且從未載入任何“新”數據 - 它是歷史記錄(SQL 標準,而不是企業,因此沒有分區)。因為每一位數據都需要綁定回其他系統,而且每個
RowNumber/DataDate
組合都是唯一的,這就是我的主鍵。我注意到由於我在 SSMS 表設計器中定義 PK 的方式,
RowNumber
它被列為第一和DataDate
第二。我還注意到我的碎片總是非常高~99%。
現在,因為每個
DataDate
只出現一次,我希望索引器每天只添加到頁面中,但我想知道它是否實際上是基於RowNumber
第一個索引,因此必須改變其他所有內容?
Rownumber
不是身份列,它是由外部系統生成的 int (可悲)。它在每個DataDate
.範例數據
RowNumber | DataDate | a | b | c..... 1 |2013-08-01| x | y | z 2 |2013-08-01| x | y | z ... 1 |2013-08-02| x | y | z 2 |2013-08-02| x | y | z ...
數據正在按
RowNumber
順序載入,DataDate
每次載入一個。導入過程是 bcp - 我嘗試載入到臨時表,然後從那裡按順序選擇 (
ORDER BY RowNumber, DataDate
) 但仍然出現高碎片。
PK 索引中列的順序是否重要?
是的,它確實。
預設情況下,主鍵約束在 SQL Server 中由唯一聚集索引強制執行。聚集索引定義表中行的邏輯順序。可能會添加許多額外的索引頁來表示 b 樹索引的上層,但聚集索引的最低(葉)層只是數據本身的邏輯順序。
為了清楚起見,頁面上的行不一定以聚集索引鍵順序物理儲存。頁中有一個單獨的間接結構,用於儲存指向每一行的指針。此結構按聚集索引鍵排序。此外,每個頁面都有一個指向聚集索引鍵順序中同一級別的上一頁和下一頁的指針。
使用 聚集的主鍵
(RowNumber, DataDate)
,行首先按邏輯排序RowNumber
,然後按DataDate
- 所以所有行在RowNumber = 1
邏輯上分組在一起,然後是行,RowNumber = 2
依此類推。當您添加新數據(
RowNumbers
從 1 到 n)時,新行在邏輯上屬於現有頁面,因此 SQL Server 可能需要做大量工作來拆分頁面以騰出空間。所有這些活動都會產生大量額外的工作(包括記錄更改)而無濟於事。拆分頁面也開始時大約 50% 是空的,因此過度拆分也會導致頁面密度低(每頁的行數少於最佳值)。這不僅是從磁碟讀取的壞消息(低密度 = 更多要讀取的頁面),低密度頁面在記憶體時也會佔用更多記憶體空間。
將聚集索引更改為
(DataDate, RowNumber
) 意味著新數據(可能高於DataDates
目前儲存的數據)將附加到新頁面上聚集索引的邏輯末端。這將消除拆分頁面的不必要成本並導致更快的載入時間。更少的碎片數據還意味著預讀活動(在進行中的查詢需要它們之前從磁碟讀取頁面)可以更有效。如果不出意外,您的查詢
DataDate
比RowNumber
. 上的聚集索引支持(然後)(DataDate, RowNumber
上的索引查找。現有的安排僅支持搜尋(並且可能僅支持搜尋)。更改主鍵後,您很可能可以刪除現有的非聚集索引。聚集索引將比它替換的非聚集索引更寬,因此您應該進行測試以確保性能仍然可以接受。DataDate``RowNumber``RowNumber``DataDate``DataDate
使用 導入新數據時
bcp
,如果導入文件中的數據按聚集索引鍵排序(理想情況下(DataDate, RowNumber
),您可能會獲得更高的性能)並指定bcp
選項:-h "ORDER(DataDate,RowNumber), TABLOCK"
為了獲得最佳數據載入性能,您可能會嘗試實現最少記錄的插入。有關更多資訊,請參閱:
- Robert Sheldon 的SQL Server 索引基礎知識
- Michelle Ufford 的有效聚集索引
- Robert Sheldon通過 TSQL 批量插入
- 使用 INSERT 進行最小化日誌記錄…由我選擇到空聚集表中
- 使用 INSERT…SELECT 和快速載入上下文的最小日誌記錄由我