sql-server - 聚集索引对数据库性能的影响

标签 sql-server database-design indexing sql-server-2000

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

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

Sequence  numeric(18, 0)
Date      datetime
Client    varchar(9)
Hash      tinyint


该表在正常操作过程中会经历很多插入。

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

另外,我真的看不到有什么好处,因为必须经常查询所有这些字段以证明聚簇索引正确,对吗?

因此,基本上来说,当我说服权力使他们相信应该改变桌子的设计时,需要一些弹药。

最佳答案

聚集索引应包含查询最多的列,以使查找或使非聚集索引有最大的机会覆盖查询中的所有列。

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

你说


另外,我真的看不到有什么好处,因为必须经常查询所有这些字段以证明聚簇索引正确,对吗?


这不是真的。可以通过使用聚集索引的第一列或第二列进行查找。它可能是一个范围搜索,但仍然是一个搜索。您不必指定所有列即可获得此好处。但是列的顺序确实很重要。如果您主要在Client上查询,那么Sequence列是不正确的选择,因为它是聚簇索引中的第一列。第二列的选择应该是与第一列一起查询最多的项目(而不是其本身)。如果发现自己查询第二列的频率几乎与第一列相同,那么非聚集索引将有所帮助。

正如其他人所说,尽可能减少聚集索引中的列/字节数很重要。

Sequence是一个随机值而不是递增值,这太糟糕了,但这可能无济于事。除非您的应用程序可以开始将其用作该表的主要查询条件(否则),否则答案不是要抛出一个身份列。现在,由于您受困于此随机序列列(假定它是最常查询的列),因此让我们看一下您的另一条语句:


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


并非完全如此。

磁盘上的物理位置并不是我们在这里真正讨论的,但是它确实在碎片方面发挥了作用,这是性能的隐含意义。

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

我相信将datetime作为第二个CI列可能会有所帮助,因为您仍然需要处理需要在两个不同Sequence值之间进行拆分的页面,但这并不像CI中的第二列也是随机的那样糟糕,因为可以保证每次插入时都能进行页面拆分,在该处您可以将行添加到页面上,从而获得升值,因为下一个序列号从下一页开始。

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

最后,一些设计调整也可能会有所帮助(我认为):


将“序列”列更改为bigint,以为每行保存一个字节(8个字节,而不是9个字节)。
使用具有4字节int身份列而不是varchar(9)的Client查找表。每行节省5个字节。如果可能,请使用smallint(-32768至32767),它是2个字节,每行最多节省7个字节。


简介:CI应该从查询最多的列开始。可以从配置项中删除所有列。尽可能缩短列(字节)。使用较低的填充因子来缓解由“随机序列”列(如果由于查询最多而必须保留在第一列)引起的页面拆分。

哦,让您的在线磁盘整理变得更加容易。如果无法更改表格,则至少可以对其进行频繁的重组,以使其保持最佳状态。也不要忽略统计信息,因此引擎可以选择适当的执行计划。

更新

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

现在,marc_s建议仅添加一个附加的int身份列作为聚簇索引。可能通过使所有非聚集索引每页获得更多行来帮助,但这完全取决于您希望性能在哪里,因为这将保证表上的每个查询都必须使用书签。查找,您将永远无法找到表。

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

归根结底,这一切都取决于确切的系统及其独特的数据访问模式,以及有关要优化哪些部分的决策。对于某些系统,只要选择始终快速,插入速度较慢也不错。对于其他人,选择时间一致但稍慢一些比选择时间略快但不一致的更为重要。对于其他人来说,直到将数据推送到数据仓库之前,它们才真正被读取,因此插入必须尽可能快。此外,性能不仅与用户等待时间甚至查询响应时间有关,而且还与服务器资源有关,特别是在大规模并行的情况下,还涉及服务器资源,因此总吞吐量(例如,每个时间单位的客户端响应)比任何其他因素都重要。

关于sql-server - 聚集索引对数据库性能的影响,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3293156/

相关文章:

sql-server - 使用 BEGIN/END 来组织 SQL 代码有什么坏处吗?

mysql - MSSQL 和 MySQL 之间使用正则表达式的 SQL 表达式区别

database - 在开发新系统时-是否应始终与利益相关者讨论数据库模式?

php - 如何使复合外键(不是复合主键)在mysql中唯一

mysql - Assets 管理数据库设计

mysql - 尽管 where 子句中的列上存在索引,但事件记录查找间歇性变慢

c++ ->4D 阵列 MatLab 的巨大性能损失的原因?

python - 动态构建索引以对 pandas 中的记录进行分类

sql-server - 带有标识(自动增量)列的批量插入

SQL通过连续递增序列拆分数据,然后按模式对每个数据进行子集化