我有一个表Item
,其中包含autoinc int主键Id
和外键UserId
。
我有一个表User
,其中包含autoinc int主键Id
。
默认是Item.Id
的索引聚集在一起。
我将主要查询user-id
上的项目,所以我的问题是:将UserId
外键索引设置为群集会更好吗?
答案 0 :(得分:2)
在标识字段上具有聚簇索引具有以下优点:记录将按创建它们的顺序存储。在表的末尾添加了新记录。
如果使用外键作为聚簇索引,则记录将以该顺序存储。创建新记录时,如果在中间插入记录,数据将被分段,这会降低性能。
如果你想要外键的索引,那么只需为它添加一个非聚集索引。
答案 1 :(得分:0)
在主键上创建聚簇索引,因此您可以将其保留为聚簇,然后在项目的用户ID上创建非聚集索引。用户仍然会非常快。 Id列将是聚簇索引。
答案 2 :(得分:0)
可能。
item.user-id
列是否是项目表中的唯一列?如果不是,您需要通过向密钥添加第二个(可能更多)列来使其成为独特的/可能这会增加您未预料到的额外开销。
与item.id
列有任何关系吗?如果是这样,那些对您的应用程序的性能可能很重要,所以应该考虑到这一点。
item.user-id
值多久可能发生变化?如果不是,那对它有利;它更容易被更新变得更糟,因为这会导致碎片化。
我的建议是使用常规item.id
作为群集密钥构建您的应用,稍后一旦您尝试了一些数据(在使用您的生产数据副本的测试系统中)切换聚集索引并测试其影响;通过这种方式,您可以轻松地查看实际结果,而不是尝试猜测多种可能性。这样可以避免过早优化/确保您做出正确的选择。
答案 3 :(得分:0)
通常,您希望在最常访问的索引上进行聚类。但是你根本不需要拥有聚类索引。您(或您的DBA)需要评估事物并权衡利弊,以便选择最合适的索引策略。
如果你在像identity
列这样的单调计数器上进行聚类,那么所有新行都将插入到表的末尾:这意味着一个热点"因为每个执行插入操作的SPID都会访问相同的数据页,所以可能会在插入时导致锁争用。
没有聚类索引的表将其数据页组织为堆,几乎只是数据页的链接列表。
SQL Server索引是B树。对于非聚簇索引,B树的叶节点是指向适当数据页的指针。这意味着如果使用了索引并且没有覆盖查询的列,则必须进行额外的外观以获取数据页。这意味着额外的I / O和分页。
聚簇索引是不同的:它们的叶节点是数据页本身,这意味着堆基本上消失了:表扫描意味着遍历聚类索引的B树。优点是,一旦您在聚簇索引中找到了所需的内容,就已经拥有了所需的数据页面,从而避免了对非聚集索引的搜索可能需要的额外I / O.当然,缺点是聚簇索引较大,因为它使用它来处理整个表,因此聚簇索引的遍历更加昂贵。
答案 4 :(得分:0)
答案仅取决于使用场景。例如,Guffa告诉数据将是零散的。那是错的。如果您的查询主要依赖于UserId,那么由ItemId聚类的数据会为您分段,因为同一用户的项目可能会在很多页面上传播。
当然,与顺序ItemId(如果它在模式中是顺序的)相比,使用UserId作为集群密钥可能会在插入时导致页面拆分。这是最多两次额外的页面写入。但是,当您由某个用户进行选择时,他的项目可能会碎片化数十页(取决于每个用户的项目,项目大小,插入策略等),因此会有大量的页面读取。如果每单个插入(非常常用的web / olap场景)有这样的选择,那么与在页面拆分上花费的少量操作相比,您可以面对数百个IO操作。这就是为集群索引创建的,不仅仅是代理ID集群。
所以没有明确的答案,你的案例中的集群UserId是好还是坏,因为这在很大程度上取决于上下文。选择/插入操作之间的比例是多少?如果由itemid聚集,用户ID是多么分散?表中有多少额外的指标,因为sql server中存在陷阱(下面)。
您可能知道,聚集索引需要唯一值。这不是一个大问题,因为您可以在pair(UserId,ItemId)上创建索引。聚簇索引本身并不存储在磁盘上,因此无论有多少字段。但是非聚集索引在其叶子中存储聚簇索引值。因此,如果你有UserId + ItemId上的聚集索引(让我们想象它们的类型是[int],大小是8字节)和ItemId上的非聚集索引,那么这个索引将有两倍的大小(每个b树叶8个字节)与仅作为聚簇索引的ItemId相比(每个叶子4个字节)。