u23*_*534 19 performance sql-server database-internals index-tuning heap performance-tuning
对于具有标识列的表,是否应该为标识列创建聚集或非聚集 PK/唯一索引?
原因是将为查询创建其他索引。使用非聚集索引(在堆上)并返回索引未涵盖的列的查询将使用较少的逻辑 I/O (LIO),因为没有额外的聚集索引 b 树查找步骤?
create table T (
Id int identity(1,1) primary key, -- clustered or non-clustered? (surrogate key, may be used to join another table)
A .... -- A, B, C have mixed data type of int, date, varchar, float, money, ....
B ....
C ....
....)
create index ix_A on T (A)
create index ix_..... -- Many indexes can be created for queries
-- Common query is query on A, B, C, ....
select A, B
from T
where A between @a and @a+5 -- This query will have less LIO if the PK is non-clustered (seek)
select A, B, C
from T
where B between @a and @a+5
....
Run Code Online (Sandbox Code Playgroud)
身份列上的集群 PK 很好,因为:
它单调增加,因此插入时不会出现页面拆分。据说批量插入可以像在堆(非集群)表上一样快
它很窄
但是,如果不将其设置为集群,问题中的查询会更快吗?
** 更新:** 如果Id是其他表的 FK 并且会在某些查询中加入怎么办?
Jul*_*eur 16
默认情况下,PK 是集群的,在大多数情况下,这很好。但是,应该问哪个问题:
PK 和聚集索引是两个不同的东西:
现在我们结束了 2 个问题:
这取决于如何:
首先,你需要一个聚集索引吗?如果批量插入,将无序数据存储到 HEAP 会更有效(相对于集群中的有序数据)。它使用 RID(行标识符,8 字节)来唯一标识行并将其存储在页面上。
聚集索引不应是随机值。叶级的数据将按索引键存储和排序。因此它应该不断增长以避免碎片或页面分裂。如果这不能通过 PK 实现,则应考虑将另一个密钥作为集群候选。从顺序的角度来看,identy 列上的聚集索引、顺序 GUID 甚至插入日期之类的东西都很好,因为所有行都将添加到最后一个叶页。另一方面,虽然唯一标识符作为 PK 可能对您的业务需求有用,但它们不应该聚集在一起(它们是随机排序/生成的)。
如果经过一些数据和查询分析,您发现在聚簇PK中进行键查找之前,您大多使用相同的索引来获取数据,则可以将其视为聚簇索引,尽管它可能无法唯一标识您的数据。
聚集索引键由您要索引的所有列组成。如果没有唯一约束(重复的增量值,否则为 null),则添加一个 uniqueifier 列(4 个字节)。然后,此索引键将为所有非聚集索引的叶级别的每一行存储一次。其中一些还会在索引树(B 树)的根和叶级之间的中间级(分支)存储多次。如果key太大,所有的非聚集索引都会变大,需要更多的存储空间和更多的IO、CPU、内存,...如果你有姓名+生日+国家的PK,很可能是这个key不是一个好的候选人。它对于聚集索引来说太大了。使用 NEWSEQUENTIALID() 的 Uniqueidentifier 通常不被视为窄键(16 字节),尽管它是连续的。
然后,一旦您弄清楚如何唯一标识表中的行,您就可以添加一个 PK。如果您认为不会在查询中使用它,请不要将其创建为集群。如果您有时需要查询它,您仍然可以创建另一个非聚集索引。请注意,PK 将自动创建唯一索引。
非聚集索引将始终包含聚集键。但是,如果索引列(+键列)被覆盖,则聚集索引中将不会有任何键查找。不要忘记您还可以将 Include 和 Where 添加到非聚集索引。(明智地使用它)
聚集索引应该是唯一的并且尽可能窄 聚集索引不应该随着时间的推移而改变并且应该增量插入。
现在是时候编写一些 SQL 来创建表、聚集和非聚集索引和约束。
这都是理论上的,因为我们不知道您使用的数据模型和数据类型(A 和 B)。
Pau*_*ite 11
对于在标识列上具有主键 (PK) 的表,默认情况下将对其进行集群。它会比非集群更好吗?
如果您问标识列(特别是)上主键的默认值是否应该是非聚集的,我会说不。大多数表受益于聚集索引,因此将聚集作为主键约束的默认值总体上可能会有所帮助,尤其是对于 SQL Server 的新用户。
与几乎所有选项一样,总是存在不同的情况,其中一个优先于另一个,但有经验的 DBA 应该知道默认值,并能够在适当的时候覆盖它。另请参阅相关问答,何时应将主键声明为非聚集的?.
如果不将其设置为集群,问题中的查询会更快吗?
是的,但有警告。
RID 查找确实比 Key 查找更有效。即使所有必需的页面都在内存中(很可能对于索引的上层),也存在与导航聚集索引 b 树相关的 CPU 成本。因此,SQL Server 通常可以在单位 CPU 时间内执行比键查找更多的 RID 查找。
在决定是否将表构造为堆时,上述内容通常不是决定因素。考虑到硬件环境和工作负载,避免查找(使用覆盖索引)是不切实际的,并且查找的数量必须足够大才能对性能产生可测量的(且重要的)影响。
在这个答案中涵盖堆与聚集索引辩论的所有方面并不实际,但我会说一般来说,倾向于将表构建为堆的理由相对较少。对我来说,选择问题中提出的那种设计需要在实施之前进行非常仔细的分析,并且必须达到很高的标准。关于“可扩展性”的一般论点是不够的。
关于连接问题的更新,评估丢失聚集索引对执行计划的影响将构成上述分析的一部分。如果使用嵌套循环连接,在连接键上使用聚集索引非常方便,因为行中的所有列都可以立即使用而无需查找。
我自己的经验是,在标识列上拥有唯一的聚集索引通常是有益的,所有事情都被考虑在内。我发现堆在空间管理方面存在问题,我还应该提到一些 SQL Server 功能需要唯一的聚集索引才能运行。
实际上,您不需要创建聚集索引或主键,因为唯一索引和非唯一索引可以处理这些工作。SQL Server 至少从 1.1 版开始就支持聚集索引,但主键只是程序员通过定义唯一索引来强制执行的“概念”。
但似乎主键和聚集索引在大多数数据库中都是有价值的概念。
让我们查看 SQL Server 文档以查看一些索引选项的部分描述,如下所示。
聚集索引: https : //msdn.microsoft.com/en-us/library/ms190457.aspx
主键: https : //msdn.microsoft.com/en-us/library/ms190457.aspx
一张表只能包含一个 PRIMARY KEY 约束。
在 PRIMARY KEY 约束中定义的所有列都必须定义为 NOT NULL。
主键可以创建为聚集索引(如果没有聚集索引,则为默认值)或非聚集索引。
唯一索引: https : //msdn.microsoft.com/en-us/library/ms187019.aspx
创建 UNIQUE 约束时,默认情况下会创建一个唯一的非聚集索引来强制执行 UNIQUE 约束。
如果表的聚集索引不存在,您可以指定一个 UNIQUE 聚集索引。
这意味着您关于聚集索引和主键的问题实际上与以下一些问题有关。请注意,并非每个表都受益于相同的索引计划。
我什么时候可以从主键与聚集索引分开中受益?
也许当聚集索引很宽时(例如,5 列文本信息,但主键很小(INT 或 BIGINT),如您所描述的那样。
你应该让主键单独作为聚集索引吗?
如果你有一个小的主键(INT 或 BIGINT)并且是聚集索引,那么聚集列的开销相对较小。尽管这种情况下的 Clustered Primary Key 也将存在于该表的每个索引中,但它比上面讨论的 Wide Cluster 付出的代价要小。
这个主键聚集索引通常不会直接提供一个简单的路径来连续选择多行。
既然您已经创建了聚集主键,那么您曾经计划包含在聚集索引中的那些其他列呢?
根据需要创建唯一(或非唯一)索引,以索引列 C1、C2、C3、C4、C5 的广泛搜索条件。这个“模仿聚集”索引中的值可以作为这 5 列的更快搜索路径。如果有一两个非索引列也被定期选择,则可以将它们包含在索引中 INCLUDE (Doctor_Name, Diagnosis_Synopsis)。
虽然我发现简单的聚集索引和主键很有用,但有一些很好的理由来考虑是在表中还是在数据库中使用它们。
你真的需要聚集索引吗?
如果您创建索引(唯一索引和非唯一索引)并定义主键而没有作为聚集索引的开销,您可能会发现较窄的索引为您提供查询所需的内容。
聚集索引和主键中有一些有用的行为,但请记住,真正重要的是索引。设计索引策略以考虑应用程序的实际情况。也许OneBigTable需要使用与大多数表不同的索引策略。
如果没有聚集索引,您的数据将存储为带有行标识符 (RID)的堆,这根本不是一个好的搜索机制。但是,如前所述,您可以创建唯一和非唯一索引来处理您的查询。
现在带你考虑堆:
堆和索引: https : //msdn.microsoft.com/en-us/library/hh213609.aspx
但是,如果您在大数据集中也有一些“热点”,您还可以查看另一种类型的索引:
过滤索引: https : //msdn.microsoft.com/en-us/library/cc280372.aspx
设计良好的过滤索引可以提高查询性能和执行计划质量,因为它比全表非聚簇索引小并且具有过滤统计信息。过滤统计比全表统计更准确,因为它们只覆盖过滤索引中的行。
过滤索引有许多限制,这些限制在过滤索引链接中列出。
但是,如果您有兴趣考虑完全跳过主键和聚集索引的可能性,您可以阅读下面链接的 Markus Winand 的帖子。他通过一些代码示例展示了他的理由,建议有时放弃使用这些功能可能是个好主意。
http://use-the-index-luke.com/blog/2014-01/unreasonable-defaults-primary-key-clustering-key
但这一切最终都会回到理解您的应用程序和设计代码、表、索引等以适应您正在做的工作上。