聚集指数对DB性能的影响

Ava*_*his 9 sql-server indexing database-design sql-server-2000

我最近参与了一个使用SQL Server 2000进行数据存储的新软件项目.

在审查项目时,我发现其中一个主表在其主键上使用聚簇索引,该索引由四列组成:

Sequence  numeric(18, 0)
Date      datetime
Client    varchar(9)
Hash      tinyint
Run Code Online (Sandbox Code Playgroud)

该表在正常操作过程中经历了大量插入.

现在,我是一名C++开发人员,而不是数据库管理员,但我对此表设计的第一印象是,将这些字段作为聚簇索引对插入性能非常不利,因为数据必须在物理上重新排序每个插入.

另外,我不能真正看到任何好处,因为人们必须经常查询所有这些字段以证明聚簇索引的合理性,对吧?

因此,基本上我需要一些弹药,以便在我找到能够说服他们应该改变桌子设计的能力时.

Eri*_*ikE 16

聚簇索引应包含最常查询的列,以便最大程度地搜索或使非聚簇索引覆盖查询中的所有列.

主键和聚簇索引不必相同.它们都是候选键,表通常有多个这样的键.

你说

另外,我不能真正看到任何好处,因为人们必须经常查询所有这些字段以证明聚簇索引的合理性,对吧?

这不是真的.只需使用聚簇索引的第一列或第二列即可进行搜索.它可能是一个范围寻求,但它仍然是一个寻求.您不必指定它的所有列以获得该好处.但是列的顺序确实很重要.如果您主要在客户端上查询,那么序列列是聚簇索引中的第一个选项.第二列的选择应该是与第一列一起查询最多的项目(不是单独的).如果您发现第二列几乎与第一列一样经常查询,那么非聚簇索引将有所帮助.

正如其他人所说的那样,尽可能减少聚簇索引中的列/字节数是很重要的.

序列是随机值而不是递增,这太糟糕了,但可能无法帮助.答案不是引入标识列,除非您的应用程序可以开始将其用作此表的主要查询条件(不太可能).现在,既然你坚持使用这个随机序列列(假设它是最常查询的),那么让我们看看你的另一个语句:

将这些字段作为聚簇索引对插入性能非常不利,因为必须在每个插入上对数据进行物理重新排序.

这不完全正确.

磁盘上的物理位置并不是我们在这里所讨论的,但它确实在碎片方面发挥作用,这一种性能影响.

每个8k页面内的行不是有序的.只是每个页面中的所有行都小于下一页,而不是前一页.插入行并且页面已满时会发生此问题:您将获得页面拆分.引擎必须将插入行之后的所有行复制到新页面,这可能很昂贵.使用随机密钥,您将获得大量页面拆分.在重建索引时,可以通过使用较低的fillfactor来改善问题.您必须使用它来获得正确的数字,但70%或60%可能比90%更好.

我认为将datetime作为第二个CI列可能是有益的,因为您仍然需要处理需要在两个不同的Sequence值之间拆分的页面,但它并不像CI中的第二列也是随机的那样糟糕,因为你可以保证在每个插入页面上进行页面拆分,如果可以将行添加到页面,则可以获得升序,因为下一个序列号从下一页开始.

缩短表中所有列的数据类型和数量以及非聚簇索引也可以提高性能,因为每页更多行=每个请求的页面读取次数更少.特别是如果发动机被迫进行表扫描.将一堆很少查询的列移动到单独的1-1表中可能会对您的某些查询产生奇迹.

最后,有一些设计调整也可能有所帮助(在我看来):

  • 将Sequence列更改为bigint以为每行保存一个字节(数字为8个字节而不是9个).
  • 对于具有4字节int标识列而不是varchar(9)的Client,请使用查找表.这样每行可节省5个字节.如果可能的话,使用一个2字节的smallint(-32768到32767),每行甚至可以节省7个字节.

摘要:CI应从最常查询的列开始.从CI中删除任何列.尽可能缩短列(字节).使用较低的fillfactor来缓解由随机Sequence列导致的页面拆分(如果由于被查询最多而必须保持优先).

哦,让你的在线碎片整理.如果表格无法更改,至少可以对其进行重新组织以使其保持最佳状态.也不要忽视统计数据,因此引擎可以选择适当的执行计划.

UPDATE

要考虑的另一个策略是,是否可以将表中使用的组合键转换为int,并创建值的查找表.假设少于所有4列的一些组合在超过100行中重复,例如,Sequence + Client + Hash但仅具有不同的Date值.然后插入到具有标识列的单独SequenceClientHash表可能有意义,因为那样您可以查找一次人工密钥并反复使用它.这也会使您的CI仅在最后一页(yay)上添加新行,并在所有非聚簇索引(yippee)中重复减少CI的大小.但这只会在某些狭隘的使用模式中有意义.

现在,marc_s建议只添加一个额外的int identity列作为聚簇索引.这可能有助于使所有非聚簇索引每页获得更多行,但这一切都取决于您希望性能的确切位置,因为这可以保证表上的每个查询都必须使用书签查找,你永远不会得到一个表寻求.

关于"大量的页面拆分和糟糕的索引碎片":正如我已经说过的,这可以通过较低的填充因子来改善.此外,频繁的在线索引重组(与重建不同)可以帮助减少这种影响.

最终,这一切都归结为确切的系统及其独特的数据访问模式,以及您希望优化哪些部分的决策.对于某些系统,只要选择总是很快,具有较慢的插入就不错.对于其他人来说,选择时间一致但稍慢一些比选择时间略快但不一致更重要.对于其他人来说,数据在被推送到数据仓库之前并没有真正读取,所以插入需要尽可能快.并且添加到混合中的是,性能不仅仅是关于用户等待时间甚至是查询响应时间,还涉及服务器资源,尤其是在大规模并行性的情况下,因此总吞吐量(例如,在每个时间单元的客户端响应中)比任何其他因素更重要.


Mik*_* M. 5

聚集索引(CI)在不断增加的,狭窄的,很少变化的值上工作得最好.您希望您的CI涵盖在使用> =,<=或BETWEEN语句的查询中最常出现的列.

我不确定您的数据通常会如何受到影响.大多数情况下,您会在IDENTITY列或其他窄列上看到CI(因为此列也将"粘贴"到所有非聚集索引上,并且我们不希望每次获取都添加大量数据如果不需要).有可能数据可能在日期最常被查询,这可能是一个不错的选择,但所有四列可能都不正确(我可能会强调,因为我不知道设置;这可能没有任何东西错了).这里有一些指示:http://msdn.microsoft.com/en-us/library/aa933131%28SQL.80%29.aspx