2011-07-18 110 views
6

这个问题涉及设计在SQL Server 2005SQL Server的非聚集索引设计

非聚集索引我有几百万行一大桌。行仅被读取或插入。大多数操作都是读取。我一直在寻找访问表的各种SELECT查询,目的是提高读取访问速度。磁盘空间并不是真正的问题。 (每一行都有一个唯一的ID,我用它作为聚集索引中的单个字段。)

我的问题是,如果非聚集索引索引比查询使用的列更多的列,进入比查询完全匹配的索引更慢的查询执行?

随着不同查询的数量增加,在WHERE子句中使用的列的排列数量也增加。我不确定使用少量列的索引(每个查询一个索引)与更多列索引较少之间的权衡。

例如,假设我有两个SELECT查询。第一个使用WHERE子句中的列A,B,C和D,第二个使用A,B,E和F.这里最好的做法是定义两个索引,一个在A/B/C/D和另一个在A/B/E/F;或A/B/C/D/E/F上的单一索引?

+0

伟大的问题!这不是一个真正的答案,但是Kimberly Tripp在SQL索引编写方面写了很多精彩的文章,你可能想看看。这里只有一个 - http://www.sqlskills.com/blogs/kimberly/Default.aspx#p4 – Yuck

回答

3

首先,索引中列的顺序很重要。因此,相应地构建/调整查询将使您能够充分利用您构建的索引。

是否分别具有两个索引或一个索引取决于争用中列的依赖性以及运行的查询类型。在你的例子中,如果E和F列涉及或依赖于C和D列,那么有一个索引覆盖所有列是有意义的。

+0

感谢您的回答!两个后续问题:1.当你说列的顺序很重要时,你的意思是列的出现顺序或排序顺序,还是两者? 2.当你说“如果E和F列涉及或依赖于C和D”,那么什么样的关系是重要的? (说)C/D/E/F中的值是相互独立的,但每列都有重复。 –

+0

1.是列出现在您的CREATE INDEX语句中的顺序。确保您的查询在WHERE子句中使用相同的顺序,以便从索引中获得最大利益。 2.当我说域之间的关系时,一个简单的例子就是找到Jason Bourne,你可以使用一个索引,它使用索引中包含LASTNAME,FIRSTNAME的顺序,然后使用查询WHERE LASTNAME ='Bourne'AND FIRSTNAME ='杰森”。如果您认为使用WHERE的顺序与上面相反,将无法利用指数获得全部收益。 –

+0

(... contd)列索引的顺序取决于业务。想象一个拥有70%姓名BOURNE的城市。那么在orser中使用带有列FIRSTNAME,LASTNAME的索引实际上是有意义的。 –

1

我的问题是,如果非聚集索引索引比查询使用的列更多的列,是否会转化为比查询完全匹配的索引更慢的查询执行?

否,具有多个列中的查询时间对于使用在索引中的第一个1,2,n列的查询不会减慢。话虽如此,如果你在内存上受到限制,索引加载到内存中可能会将其他内容推出内存并减慢查询速度,但如果你有足够的内存,这应该不成问题。

随着不同查询的数量增加,其WHERE子句中使用的列的排列数也增加。我不确定使用少量列的索引(每个查询一个索引)与更多列索引较少之间的权衡。

你应该先添加最常查询的唯一字段到索引。 很多列的索引可能不会给你想要的东西。

例如,如果你有以下的列的索引:

  • ColumnA
  • ColumnB
  • ColumnC
  • ColumnD
  • ColumnE
  • ColumnF

,按照ColumnA,ColumnB,ColumnC,ColumnD ...的查询筛选将使用索引,但是如果您只是针对ColumnE或ColumnF查询,它将不会使用索引。

乘坐不同影响的方法,如果你有一个表使用6个索引中的每个只有一列

  • 指数1 - ColumnA
  • 索引2 - ColumnB
  • INDEX3 - ColumnC
  • Index4 - ColumnD
  • 索引5 - 列E
  • 索引6 - 列F

在这种情况下,这6个索引中只有一个将用于任何查询。

另外,如果您的索引包含的值不是很有选择性,那么它可能不会帮助您。例如,如果您有一个名为GENDER的列可能包含以下值(男性,女性和未知),那么它可能不会帮助您将此列包含在索引中。当运行查询时,SQL Server可能会确定它们的列不够具有选择性,只是假定全表扫描会更快。

有许多方法可以找出查询使用的索引,但我使用的一种方法是查看从不使用的索引。在数据库中运行以下查询,并确定您认为正在使用的索引是否真的被使用。

SELECT iv.table_name, 
     i.name       AS index_name, 
     iv.seeks + iv.scans + iv.lookups AS total_accesses, 
     iv.seeks, 
     iv.scans, 
     iv.lookups, 
     t.indextype, 
     t.indexsizemb 
FROM (SELECT i.object_id, 
       Object_name(i.object_id) AS table_name, 
       i.index_id, 
       SUM(i.user_seeks)  AS seeks, 
       SUM(i.user_scans)  AS scans, 
       SUM(i.user_lookups)  AS lookups 
     FROM sys.tables t 
       INNER JOIN sys.dm_db_index_usage_stats i 
        ON t.object_id = i.object_id 
     GROUP BY i.object_id, 
        i.index_id) AS iv 
     INNER JOIN sys.indexes i 
      ON iv.object_id = i.object_id 
      AND iv.index_id = i.index_id 
     INNER JOIN (SELECT sys_schemas.name AS schemaname, 
          sys_objects.name AS tablename, 
          sys_indexes.name AS indexname , 
          sys_indexes.type_desc AS indextype , 
    CAST(partition_stats.used_page_count * 8/1024.00 AS DECIMAL(10, 3)) AS indexsizemb 
FROM sys.dm_db_partition_stats partition_stats 
INNER JOIN sys.indexes sys_indexes 
    ON partition_stats.[object_id] = sys_indexes.[object_id] 
     AND partition_stats.index_id = sys_indexes.index_id 
     AND sys_indexes.type_desc <> 'HEAP' 
INNER JOIN sys.objects sys_objects 
    ON sys_objects.[object_id] = partition_stats.[object_id] 
INNER JOIN sys.schemas sys_schemas 
    ON sys_objects.[schema_id] = sys_schemas.[schema_id] 
     AND sys_schemas.name <> 'SYS') AS t 
ON t.indexname = i.name 
AND t.tablename = iv.table_name 
--WHERE t.IndexSizeMB > 200 
WHERE iv.seeks + iv.scans + iv.lookups = 0 
ORDER BY total_accesses ASC; 

我一般追查从未被使用,或者SQL服务器重新启动后尚未使用几个月指标,并确定它们是否应该删除或没有。有时,太多的索引可能会减慢SQL Server计算出运行查询的最佳路径的速度,并且删除未使用的索引可以加速该过程。

我希望这有助于理解您的索引。

1

现有的答案已经非常好。这里有一个新想法:在某个工作负载和内存可用性下寻找一组最佳索引是一个难以解决的问题,需要对大型搜索空间进行彻底搜索。

The 数据库引擎优化顾问(DTA)实现了这一点!我建议你记录代表性的工作量(包括写作!),让DTA给你建议。它也会占用磁盘空间。

1

磁盘空间并不是真正的问题。

请不要这样想。如果您拥有500 GB的可用空间,则无关紧要。表或索引越大,从磁盘读取所花费的时间越多,并且占用内存(即缓冲池)的空间越多,并且为了满足查询所需的逻辑读取越多。有关此主题的更多信息,请看这里: http://www.sqlservercentral.com/articles/data-modeling/71725/

(每一行都有一个唯一的ID,而我使用的是作为 聚集索引单个字段。)

最您在WHERE子句中使用该ID的查询?如果不是,那么它可能不适合聚集索引。

我的问题是,如果一个非聚集索引的索引更多的列比由查询使用 ,这是否转化为查询执行比 慢恰好与查询相匹配的指数?

这取决于几个因素。你在谈论多少个领域?单个TINYINT字段是1个字节?或者几个字段组成300字节?除非使用过滤索引,否则需要将索引大小加上聚簇索引大小(对于非UNIQUE索引)乘以行数。正如我上面提到的,占用更多空间意味着更慢,但实际上100 MB的额外5 MB可能不会有明显的差异。

请记住,索引设计既是艺术又是科学。您需要考虑哪些查询最常执行,以及使用哪些ORDER BY和WHERE子句。您需要记住,即使索引的其余字段在查询中,如果索引的前导列未出现在查询中,索引也不会被使用。

一般来说,你不想索引的每个字段分别因为:

  1. 过多的索引减慢DML操作,这是一个问题,即使大多数操作是SELECT在这个表上
  2. 太多索引增加死锁的机会
  3. 询问4个字段的查询不会使用4个单独的索引。大部分时间优化器会选择它认为将工作最好的,有时可能选择加入他们两个人在一起的人,特别是如果你有一个OR条件

例如,说我有两个SELECT查询。第一种使用列A, B,C,和d它的WHERE子句中,第二个使用A,B,E和F

你可以通过索引只是A和B,看怎么做最好这可以解决。如果这种组合是独特的,那么考虑它是一个复合聚集索引的可能性。如果它们不是唯一的,但仍然被大多数查询使用,请考虑制作聚集索引:A,B,IDfield。包括ID字段最后给出组合唯一性。这很重要,因为如果您的聚簇索引不是主键,那么您真的需要将聚簇索引声明为UNIQUE,因此它没有隐藏的唯一码字段。主键在定义上是唯一的。

另请参阅Index的INCLUDE选项。

是的,列顺序的确很重要,因为它决定了索引的组织方式。考虑一下ActionDate,CustomerID与CustomerID,ActionDate之间的区别。如果ActionDate是第一个,那么在特定日期范围内查找所有CustomerID更容易。但是,如果你只关心一个客户,并希望他们的信息有多个不同的日期,那么你将不得不跳过整个索引,因为他们的数据将被分散到整个索引中。在这种情况下,您最好使用CustomerID,因为您可以更快地缩小到他们的数据开始位置,然后根据日期获取您想要的数据。

但是,不,您的WHERE条件的顺序不会影响索引是否被使用。 SQL Server使用基于成本的优化器扫描所有条件并使用索引的统计信息(主要列)来确定最合适的计划应该是什么。

最后,一定要测试各种策略。不要只是尝试一件事,继续前进。在你的描述中你是非常普遍的 - 甚至没有给出字段的数据类型或字段的使用方式 - 所以这里的任何建议都是非常具体的,这是值得怀疑的。使用SET STATISTICS IO ON并查找逻辑读取。数字越低越好!