【问题标题】:Can I have a primary key and a separate clustered index together?我可以同时拥有一个主键和一个单独的聚集索引吗?
【发布时间】:2012-09-25 07:57:12
【问题描述】:

假设我已经有一个主键,它可以确保唯一性。我的主键也是记录的排序索引。但是,我很好奇磁盘中记录物理顺序中主键的任务(如果有)。而实际的问题是我可以为这些记录创建一个单独的聚集索引吗?

【问题讨论】:

  • 我假设您在谈论 SQL Server?并非所有 DBMS 都有“聚集索引”的概念。
  • 我没有意识到这一点。谢谢。
  • 那么您在谈论 SQL Server 吗?
  • 我以一般的方式(理论上)提出了这个问题,但我也想听听一个特定案例的答案。
  • @a_horse_with_no_name 并非所有 DBMS 都有“聚集索引”的概念让我提一下 Oracle(通过“索引组织表”)和 MySQL/InnoDB 也支持聚类。虽然,Oracle 与 PK 的联系比 MS SQL Server 更紧密,而在 InnoDB 下它根本无法关闭。 PostgreSQL 有“混合”集群——你需要resort the table periodically 来维护集群顺序。 DB2 和 Informix 也支持某些形式的集群,虽然我不太熟悉...

标签: database database-design indexing primary-key clustered-index


【解决方案1】:

这取决于您的数据库管理系统。并非所有这些都实现了聚集索引。那些这样做的人有可能以不同的方式实施它们。据我所知,每个实现聚簇索引的平台也提供了选择哪些列在聚簇索引中的方法,尽管通常主键是默认值。

在 SQL Server 中,您可以像这样创建非聚集主键和单独的聚集索引。

create table test (
  test_id integer primary key nonclustered,
  another_column char(5) not null unique clustered
  );

我认为在 Oracle 中与此最接近的是索引组织表。我可能是错的。这与在 SQL Server 中创建具有聚集索引的表不太一样。

您不能在 SQL Server 中的单个表上拥有 多个 聚集索引。表的行一次只能按一个顺序存储。实际上,我想您可以将行存储在多个不同的顺序中,但实际上您必须为每个顺序复制全部或部分表。 (虽然我在写这个答案的时候并不知道DB2 UDB supports multiple clustered indexes,这是一个相当古老的功能。它的design and implementation与SQL Server有很大的不同。)

主键的作用是保证唯一性。虽然这项工作通常是通过在主键列上创建唯一索引来完成的,但严格来说,唯一性和索引是两个不同的东西,具有两个不同的目标。唯一性旨在数据完整性;索引旨在提高速度。

主键声明并非旨在为您提供有关磁盘上 顺序的任何信息。实际上,它通常会为您提供一些关于磁盘上索引条目 顺序的信息。 (因为主键通常使用唯一索引来实现。)

如果您从具有聚集索引的表中选择行,您仍然不能保证这些行将按照它们存储在磁盘上的相同顺序返回给用户 .粗略地说,聚集索引有助于查询优化器find 行更快,但它不控制这些行返回给用户的顺序。 唯一 保证行返回给用户的顺序的方法是使用显式的ORDER BY 子句。 (这似乎是一个相当常见的混淆点。当聚集索引上的裸 SELECT 没有按照他们期望的顺序返回行时,很多人似乎感到惊讶。)

【讨论】:

  • “但是你必须为每个订单复制表格。我不知道有没有这样做的 dbms。” 实际上,类似的效果可以是通过覆盖整个表(即包含所有字段)的索引可以轻松完成。如果聚簇表是一个B-Tree,那么这个索引就是另一个B-Tree,包含相同的数据,但顺序不同,所以合适的查询根本不需要触及原来的B-Tree。当然,在实践中,您可能不会将所有字段都放在二级索引中 - 只是那些实际查询所需的字段(+ PK 字段,这是隐含的)。
  • “实际上,类似的效果可以通过一个覆盖整个表的索引来实现” SQL Server的聚集索引的叶子节点是数据页。其非聚集索引的叶节点是指向数据的索引页。我明白你的意思,但它们在物理布局和磁盘 I/O 方面并不相似,这就是问题的重点。
  • "...指向数据" 聚集表中的二级索引的组织方式与基于堆的表中的索引不同。没有可指向的堆。相反,他们通过保存集群关键字段的副本“指向”主 B-Tree。或者至少这就是 Oracle 和 InnoDB 的做法——我不知道 SQL Server 有什么特别之处吗?我希望集群索引和覆盖查询的二级索引之间的性能相似(覆盖避免双重查找集群索引)。也许这不是你的经历?
  • @BrankoDimitrijevic:你能测试一下吗? (这里没有 Windows 服务器。)我希望覆盖整个表的二级索引会有更多的磁盘 I/O,因为更少的索引条目可以放在数据页上。
  • 我终于开始测试它,结果似乎表明覆盖二级索引比表格“胖”。事实上(令我惊讶的是)它甚至看起来更苗条。当谈到复杂的 MS SQL Server 特定语法时,我当然不认为自己是专家,所以如果你能看看 my measurements 并让我知道我是否犯了任何错误,我将不胜感激......跨度>
【解决方案2】:

这是按照discussion with @Catcall 测试聚集表上covering 二级索引的大小和性能特征的尝试。

所有测试均在 MS SQL Server 2008 R2 Express 上完成(在一个功率相当不足的 VM 内)。

尺寸

首先,我用二级索引创建了一个聚集表,并用一些测试数据填充它:

CREATE TABLE THE_TABLE (
    FIELD1 int,
    FIELD2 int NOT NULL,
    CONSTRAINT THE_TABLE_PK PRIMARY KEY (FIELD1)
);

CREATE INDEX THE_TABLE_IE1 ON THE_TABLE (FIELD2) INCLUDE (FIELD1);

DECLARE @COUNT int = 1;
WHILE @COUNT <= 1000000 BEGIN
    INSERT INTO THE_TABLE (FIELD1, FIELD2) VALUES (@COUNT, @COUNT);
    SET @COUNT = @COUNT + 1;
END;

EXEC sp_spaceused 'THE_TABLE';

最后一行给了我以下结果...

name        rows        reserved    data        index_size  unused
THE_TABLE   1000000     27856 KB    16808 KB    11008 KB    40 KB

所以,索引的 B-Tree (11008 KB) 实际上小于表的 B-Tree (16808 KB)。

速度

我在表中的数据范围内生成了一个随机数,然后将其作为从表中选择整行的标准。重复 10000 次,测量总时间:

DECLARE @I int = 1;
DECLARE @F1 int;
DECLARE @F2 int;
DECLARE @END_TIME DATETIME2;
DECLARE @START_TIME DATETIME2 = SYSDATETIME();

WHILE @I <= 10000 BEGIN

    SELECT @F1 = FIELD1, @F2 = FIELD2
    FROM THE_TABLE
    WHERE FIELD1 = (SELECT CEILING(RAND() * 1000000));

    SET @I = @I + 1;
END;

SET @END_TIME = SYSDATETIME();
SELECT DATEDIFF(millisecond, @START_TIME, @END_TIME);

最后一行产生的平均时间(10 次测量)为 181.3 毫秒。

当我将查询条件改为:WHERE FIELD2 = ...,所以使用二级索引,平均时间为195.2ms。

执行计划:

因此(在 PK 上选择与在覆盖二级索引上进行选择)似乎是相似的。对于更大量的数据,我怀疑二级索引可能会稍微快一些(因为它看起来更紧凑,因此对缓存友好),但我在测试中还没有达到这一点。

字符串测量

使用 varchar(50) 作为 FIELD1FIELD2 的类型并插入长度在 22 到 28 个字符之间变化的字符串会产生类似的结果。

尺寸是:

name        rows        reserved    data        index_size  unused
THE_TABLE   1000000     208144 KB   112424 KB   95632 KB    88 KB

平均时间是:搜索FIELD1 254.7 毫秒和 296.9 毫秒 FIELD2

结论

如果一个聚簇表有一个覆盖二级索引,该索引将具有类似于表本身的空间和时间特征(可能稍微慢一些,但不会慢很多)。如果有效,您将拥有两个 B 树,它们对数据进行不同的排序,但在其他方面非常相似,从而实现拥有“第二个集群”的目标。

【讨论】:

    猜你喜欢
    • 2012-08-05
    • 1970-01-01
    • 2022-12-29
    • 2012-07-18
    • 2011-02-14
    • 1970-01-01
    • 2015-03-12
    • 2011-08-21
    • 2012-10-31
    相关资源
    最近更新 更多