【发布时间】:2015-10-23 18:59:46
【问题描述】:
假设我们必须为 Stackoverflow 问题定义最佳索引。但我们不要采用实际 Posts 表的架构,我们只包括那些实际相关的列:
create table Posts (
Id int not null
identity,
PostTypeId tinyint not null,
LastActivityDate datetime not null
default getdate(),
Title nvarchar(500) null, -- answers don't have titles
Body nvarchar(max) not null,
...
)
我已经添加了Id 作为标识,尽管Data Stackexchange shows 没有一个表对它们有主键约束,也没有标识列。有许多只是唯一/非唯一的聚集/非聚集索引。
使用场景
所以基本上两个帖子的主要场景:
- 它们按时间顺序按其
LastActivityDate列(或者可能LastEditDate,我没有包括在上面,因为它不是那么重要)按降序显示 - 它们单独显示在问题详细信息中
- 答案按投票顺序显示在问题详细信息页面上(
ScoreCount列不属于我的上层代码)
索引优化
在上述情况下最好创建哪些索引,特别是如果我们说 #1 是最常见的情况,因此它必须非常快速地工作。
我想说更好的可能性之一是创建这些索引:
-- index 1
alter table Posts
add primary key nonclustered (Id);
-- index 2
create clustered index IX_Posts_LastActivityDate
on Posts(LastActivityDate desc);
-- index 3
create index IX_Posts_ParentId
on Posts(ParentId, PostTypeId)
include (ScoreCount);
这样我们基本上得到了三个索引,其中第二个是集群的。
所以为了让#1 工作得非常快,我在LastActivityDate 列上设置了聚集索引,因为当我们对它们进行范围比较时,聚集索引特别好。我们将按时间顺序对问题进行排序,从最新到最旧,因此我设置了排序方向,并且还在聚集索引中包含了类型。
那么我们解决了什么问题?
- 场景 #1 被索引 2 非常有效地覆盖,因为它已聚集并完全覆盖;我们还可以轻松高效地进行结果分页;
- 场景 #2 在某种程度上被唯一索引 1(获取问题)和非唯一索引 3 覆盖,以获取由
ScoreCount排序的所有相关答案(场景 #3);如果我们决定按时间顺序排列也包含在索引 2 中的答案;
问题 1
SQL 内部结构使得 SQL 隐式地将聚簇键添加到非聚簇索引,因此它可以在行存储中定位记录。
- 如果聚集索引是唯一的,那么这就是将添加到非聚集索引的键,并且
- 如果聚簇索引不唯一,SQL 应该会生成它自己的
UniqueId并使用它
由于我还在表上添加了一个非聚集主键(设计上必须是唯一的),我想知道 SQL 是否仍会在聚集的非聚集主键上提供自己的唯一键唯一索引还是会使用非聚集主键来唯一标识每条记录?
问题 2
因此,如果不使用主键来定位行存储(聚集索引)上的记录,那么实际创建 PK 是否有意义?在这种情况下,这样做会更好吗?
create unique index UX_Posts_Id
on Posts(Id);
-- include (Title, Body, ScoreCount);
还包括注释掉的列会很好,但是那样会使这个索引效率低下,因为它在缓存中会更糟......为什么我要问是否创建这个索引而不是 @ 会更好987654332@ 约束是因为我们可以在此索引中包含额外的非键列,而当我们添加内部生成唯一索引的 PK 约束时我们不能这样做......
问题 3
我知道 LastActivityDate 的变化是聚集索引所不希望的,但我们必须考虑这样一个事实,即该列在变得或多或少静态之前更有可能发生一段时间的变化,所以它应该'不会导致过多的索引碎片,因为每当 LastActivityDate 更改时,记录大多会附加到末尾。某些任意页面上的索引碎片永远不会发生,因为某些新记录将插入到某些旧(er)页面中,因为LastActivityDate 只会增加。因此,大多数修改将发生在最后一页。
所以问题是这些更改是否有害,因为 LastActivityDate 不是集群索引键的最佳候选者:
-
这不是唯一的 - 尽管有人可能会对此争论不休,尤其是如果我们将
datetime更改为datetime2并使用更高精度的函数sysdatetime()并将索引设置为unique - 很窄 - 差不多
- 它不是静态的 - 但我已经解释了它是如何变化的
- 不断增加
【问题讨论】:
标签: sql-server primary-key clustered-index sql-optimization non-clustered-index