【问题标题】:Can a Primary Key be a Non Clusterd Index on a Clustered Table?主键可以是聚集表上的非聚集索引吗?
【发布时间】:2011-08-21 21:14:03
【问题描述】:

我正在设计一个成员表来存储网站的用户。每次用户登录网站时都会使用它,并且偶尔会访问它以更新用户详细信息。

用户将使用电子邮件地址和密码登录,每个帐户都将拥有一个唯一的电子邮件地址。因此,成员表的电子邮件列应该是它的聚集索引似乎是合乎逻辑的,因为当用户登录时,该表上的大多数查询将针对电子邮件列。使 Email 列唯一和聚集索引的键应该可以在用户登录时快速查询用户的数据并提高性能。

但据我了解,将电子邮件列设置为主键是错误的,原因有两个。第一,主键应该是不变的,所以如果用户决定更改他们的电子邮件地址,那么所有外键都必须更新,这会很糟糕。其次,电子邮件地址是字符串,这会使 Join 比 PK 为 int 时更慢。

那么我可以将非聚集索引设为主键吗?这样表就既有一个以电子邮件为唯一键的聚集索引,又有一个 int 主键作为顶部的非聚集索引?

谢谢, 邓肯

【问题讨论】:

    标签: sql-server sql-server-2008 clustered-index database-design


    【解决方案1】:

    主键是一种逻辑数据库设计,只需唯一且非 NULL(通过索引实现)。

    此外,您可以选择单个聚集索引,它应该是窄的、唯一的、递增的和静态的(电子邮件可能不适合这样做)。

    我会创建一个 IDENTITY int 主键并在其上进行集群。

    我会在电子邮件上添加一个唯一的非聚集索引并“包含”其他列,以便覆盖您最频繁的繁重查询(即密码哈希)。请注意,您不需要将聚集键添加到包含的列中,因为它始终作为书签包含在非聚集索引中。

    查看执行计划以确保您没有在用户表中看到任何表扫描或聚集索引扫描。

    我要补充一点,通常人们认为看到查询使用聚集索引是一件好事。我认为在索引覆盖的查询中使用非聚集索引扫描或查找在堆(没有聚集索引的表)上与聚集索引上一样好,并且比聚集索引扫描或查找更好.我还认为聚集索引是一个名称,它会导致人们对事物做出各种假设(首先,它并不是表上的真正索引,它表明表完全存储在索引结构中)和误解关于它的重要性。聚集索引在非常大的操作中是最重要的,在这些操作中需要按聚集顺序排列大量数据。

    典型 OLTP 查询的实际(读取)查询速度来自于在查询中的所有表上使用尽可能窄的非聚集索引覆盖查询,其中每一列都以适当的顺序和查询/参数的正确排序方向。

    【讨论】:

    • +1 电子邮件对我来说似乎是一个特别糟糕的聚集索引
    • @gbn sqlskills 链接应该会在有人询问 SO 上的聚集索引时自动出现。
    • @Cade Roux 感谢您的回复,但我仍然不明白为什么电子邮件地址会成为错误的聚集索引键。为什么需要变窄?无论是聚集索引键还是非聚集索引键,电子邮件仍然会几乎一直查找用户数据,因此无论哪种方式都需要相同的计算,只有当它是非聚集索引键时一旦在该索引中找到该用户,就必须在聚集索引中再次查找该用户。如果只有 5% 的时间通过它访问数据,那么 int PK 有什么好处?
    • @FunkyFresh84,索引指向数据的位置。以一种过于简单的方式,聚集索引将加快连续行的检索,因为它们在物理上“彼此相邻”。它不会使从 single 指针中检索数据的速度更快。
    【解决方案2】:

    你绝对可以在主键中创建一个非聚集索引。

    但是,我认为你有点倒退了。 电子邮件地址将成为一个特别糟糕的聚集索引,因为它本身不是有序的。随着表格的增长,您将因页面拆分、重新排序等原因失去INSERT 的性能。

    正如@Cade Roux 所说,我会将 autonum 设为聚集索引,强制电子邮件地址的唯一性。

    编辑:聚集索引表示数据是如何以物理方式存储在磁盘上的。非顺序聚集索引会损害INSERT 的性能,因为数据必须重新排序(导致页面拆分)。 对于扫描用户表中的单行,您可能会发现聚集索引和非聚集索引之间的差异可以忽略不计。但是,根据@gbn 发布的出色链接,您可能在范围选择上具有更好的性能,因为数据是连续的。尽管如此,我个人还是需要认真考虑是否决定将字符串(或任何固有的无序数据)用于聚集索引。

    EDIT2:我能想到的一个例外情况是,如果您通常按字母顺序选择电子邮件地址的用户块...您仍然会较慢 INSERTs 但您应该能够更快地检索这些分组...正如@Cade Roux 在 cmets 中所说:您应该期望单行 SELECTs 由于聚集索引而具有更高的性能。

    【讨论】:

    • 感谢您的回复。但是我不明白电子邮件是如何不是天生有序的。当然,如果它是聚集索引键,那么通过电子邮件查找用户就像搜索字典一样。也许插入会更慢,但查找肯定会更快?
    • @FunkyFresh84 聚集索引反映了数据是如何以物理方式存储在磁盘上的。这与无序 guid 不能生成良好的聚集索引的原因相同。假设一个用户创建了 a@example.com,然后第二个用户创建了 c@example.com...第三个用户创建了 b@example.com...聚集索引必须重新排序数据才能执行插入。这与新记录在聚集索引中最后出现的自动编号形成对比。将为索引构建相同的 B-Tree;无需在电子邮件上集群
    • @FunkyFresh84 使用聚集索引查找/扫描比表扫描更快,但非聚集索引查找几乎总是更快,因为 NCI 每页可容纳更多行
    【解决方案3】:

    是的,你可以。创建表时,按如下方式设置列:

    CREATE TABLE Members
    (
      ID INT NOT NULL IDENTITY(10000,1),
      Email Varchar(200) NOT NULL CONSTRAINT pk_Members PRIMARY KEY NONCLUSTERED,
      Otherstuff ...
    )
    
    CREATE CLUSTERED INDEX cdx_Members ON Members(ID)
    

    【讨论】:

    • 感谢您的帮助,这回答了我的问题。除了我的意思是,Email 应该是聚集索引键,而 Int 应该是非聚集 PK。你有任何关于它的cmets吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-02
    • 2021-12-14
    • 2012-12-30
    • 2020-11-06
    • 2011-05-15
    • 1970-01-01
    • 2018-08-04
    相关资源
    最近更新 更多