我有一些具有相同基本结构的非常大的表。每个都有一个RowNumber (bigint)DataDate (date)列。每天晚上使用SQLBulkImport加载数据,并且从未加载过任何“新”数据-它是历史记录(SQL Standard,不是Enterprise,因此没有分区)。绑定到其他系统,每个RowNumber/DataDate组合都是唯一的,这就是我的主键。

我注意到,由于我在SSMS Table Designer中定义PK的方式,RowNumber列在第一位,DataDate第二列。

我还注意到我的碎片始终非常高〜99%。

现在,因为每个DataDate只会出现一次,所以我希望索引器只会添加到每天都有页面,但是我想知道它是否首先基于RowNumber进行索引,因此不得不转移其他所有内容吗?


Rownumber不是标识列,它是由外部系统(严重)。它在每个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),但仍然会出现碎片。

#1 楼


PK索引中的列顺序重要吗?


是的。

默认情况下,在SQL中强制执行主键约束服务器由唯一的聚集索引组成。聚集索引定义表中行的逻辑顺序。可能会添加许多额外的索引页来表示b树索引的较高级别,但是聚集索引的最低(叶)级别只是数据本身的逻辑顺序。

要清楚一点,页面上的行不一定会按照聚集索引键的顺序进行物理存储。页面内有一个单独的间接结构,用于存储指向每一行的指针。此结构按聚簇索引键排序。同样,每个页面在聚簇索引键顺序中都具有指向同一级别上一页和下一页的指针。

使用聚簇主键(RowNumber, DataDate),行在逻辑上首先按RowNumber进行排序,然后按DataDate-因此将RowNumber = 1的所有行逻辑分组,然后将RowNumber = 2的行分组,依此类推。

添加新数据(RowNumbers从1到n)时,新行逻辑上属于现有页面,因此SQL Server可能需要做大量工作来拆分页面以腾出空间。所有这些活动都会产生很多额外的工作(包括记录更改),但毫无益处。

分割的页面也从大约50%的空白开始,所以过多的分割会导致页面密度低(行数少于每页最佳)。这不仅是从磁盘读取的坏消息(较低的密度=需要读取的页面更多),而且较低密度的页面在缓存时还会占用更多的内存空间。

将聚簇索引更改为(DataDate, RowNumber意味着将新数据(可能比当前存储的DataDates高)附加到新页上的聚簇索引的逻辑端。这将消除不必要的页面拆分开销,并缩短加载时间。较少碎片的数据还意味着预读活动(在正在进行的查询需要它们之前从磁盘读取页面)可以更有效率。

如果没有其他内容,则查询的可能性更大比DataDateRowNumber上搜索。 (DataDate, RowNumber上的聚集索引支持DataDate(然后RowNumber)上的索引查找。现有的安排仅支持RowNumber上的查找(并且可能仅支持DataDate上的查找)。更改主键后,您很可能可以在DataDate上删除现有的非聚集索引。聚集索引将比其替换的非聚集索引宽,因此您应该进行测试以确保性能仍然可以接受。

使用bcp导入新数据时,如果导入中的数据可能会获得更高的性能。文件通过聚簇索引键(理想情况下为(DataDate, RowNumber)排序,然后指定bcp选项:记录的插入。有关更多信息,请参见:Robert Sheldon的SQL Server索引基础


Michelle Ufford的有效聚簇索引


>罗伯特·谢尔顿(Robert Sheldon)通过TSQL进行大容量插入

我使用INSERT…SELECT最小记录日志到空群集表中

我使用INSERT…SELECT最小记录日志…我选择并快速加载上下文
/>

评论


一个很好的答案-我现在知道我应该做什么以及为什么。我以为是这样,但不是这样!谢谢。

– BlueChippy
13年8月21日在9:05

在将DB放入我的本地SQL Server进行测试时花了很长时间:更改索引负载之前花了45分钟……之后,只花了5分钟!!!

– BlueChippy
13年8月22日在7:39

#2 楼

是的,顺序很关键。我非常怀疑您是否通过RowNumber查询(例如WHERE RowNumber=1)。绝大多数的时间序列是按日期查询的(WHERE DataDate BEWEEN @start AND @end),而这样的查询将需要按DataDate进行聚类组织。

碎片通常是一条红鲱鱼。减少碎片化不是您这里的目标,但应该为查询提供适当的组织。另外,减少碎片是一个很好的想法,但这并不是一个目标。如果您有一个组织合理的数据模型来匹配您的工作负载(您的查询已得到适当覆盖)并且您的度量显示碎片影响了性能,那么我们可以讨论一下。

评论


我在DataDate上也有一个非聚集索引,正如您所说的,它在查询中经常是WHERE子句。

– BlueChippy
13年8月21日在8:21

如果列的ORDER至关重要,那么incorrecrt订单的影响会导致我的I / O增加吗?我的想法是,它是按RowNumber排序的,因此每次都必须在索引上做很多工作,而它应该基于DataDate?

– BlueChippy
13年8月21日在8:24