【问题标题】:SQL Server insert performance with and without primary key使用和不使用主键的 SQL Server 插入性能
【发布时间】:2011-04-01 04:58:24
【问题描述】:

总结:我有一个通过以下内容填充的表格:

insert into the_table (...) select ... from some_other_table

在 the_table 上使用 no 主键运行上述查询比 使用 主键运行它快约 15 倍,我不明白为什么。

细节:我认为这最好通过代码示例来解释。

我有一张桌子:

create table the_table (
    a int not null,
    b smallint not null,
    c tinyint not null
);

如果我添加一个主键,这个插入查询非常慢:

alter table the_table
    add constraint PK_the_table primary key(a, b);

-- Inserting ~880,000 rows
insert into the_table (a,b,c)
    select a,b,c from some_view;

如果没有主键,相同的插入查询大约快 15 倍。 但是,在没有主键的情况下填充 the_table 后,我可以添加主键约束,并且只需要几秒钟。这对我来说真的没有意义。

更多信息:

  • 估计的执行计划显示在聚集索引插入上花费的总查询时间为 0%
  • SQL Server 2008 R2 开发者版,10.50.1600

有什么想法吗?

【问题讨论】:

  • 那里没有启示。删除所有索引,插入所有数据,然后重新创建索引通常比简单地插入数据要快.​​..
  • 是的。 sql-server 中的 PK 索引集群或非集群。因此,正如 Mitch 所指出的,在没有任何索引的情况下插入然后重新创建它会快得多。
  • 在没有任何唯一性约束的情况下,插入速度之快令人惊叹,尤其是在一次插入一行时。
  • 记录:您所看到的是索引的内务管理需要时间。对于每次插入,索引也必须更新。删除它并在之后添加它可以消除它。索引不是免费的。它们确实加快了搜索速度,但减慢了插入速度。更新和删除取决于具体的语句。
  • @marc_s,问题是为什么 PK 会如此严重地影响性能。 15 倍的差异对我来说似乎很陡峭。我没有具体说明这个问题,你是对的。

标签: sql-server


【解决方案1】:

实际上它并不像 Ryk 建议的那样清晰。

实际上,将数据添加到具有索引的表中比在堆中更快。

阅读这篇文章 - 据我所知,它非常受欢迎:

http://www.sqlskills.com/blogs/kimberly/post/The-Clustered-Index-Debate-Continues.aspx

请记住它是由 SQL Server MVP 和 Microsoft 区域总监编写的。

与堆相比,在聚簇表中(但仅在“正确”聚簇表中)的插入速度更快。这里的主要问题是在 IAM/PFS 中查找以确定堆中的插入位置比在聚集表中(插入位置已知,由聚集键定义)中的查找要慢。当插入到定义了顺序 (CL) 并且该顺序不断增加的表中时,插入会更快。我有一些简单的数字,但我正在考虑创建一个更大/复杂的场景并发布它们。笔记本电脑上的简单/快速测试并不总是那么“令人兴奋”。

【讨论】:

  • 丹尼尔,这是一个有趣的帖子。它不一定回答为什么插入速度较慢的问题。 RyK 做到了这一点,但它确实提供了一些有用的见解,表明事物并不总是纯粹的黑白。
  • [但只在“正确的”聚簇表中] - 他们知道如何表达他们的话,因为没有“正确的”聚簇表比拥有正确的聚簇表容易得多一。考虑一个表,其中您在 100% 的时间使用的列上有一个聚集索引,但它是一个日期(唯一)列。我认识的大多数人都会在上面放置一个聚集索引。现在用 2000 万条记录填充该表并尝试插入一条。现在将表更改为堆并查看差异。由此产生的东西,不是非黑即白,需要一些了解。
  • 好的,看来我们需要将其分解为两个答案。如果您的表有一个主键是自动递增的整数,那么这是最快的插入。如果您的表的主键不是自动增加的整数,那么您应该只使用堆而不是主键?
【解决方案2】:

我认为,如果您创建一个简单的集群主键并由单个自动递增列组成,那么插入到这样的表中可能会更快。最有可能的是,由多个列组成的主键可能是插入速度变慢的原因。当您使用复合键作为主键时,插入的行可能不会添加到表的末尾,但可能需要添加到表中现有行的物理顺序中间的某个位置,这会增加插入时间,从而使插入速度较慢。因此,在您的案例中使用单个自动递增列作为主键值来加快插入速度。

【讨论】:

    【解决方案3】:

    这是一个很好的问题,但也是一个非常糟糕的问题。在问为什么索引会减慢插入速度之前,您知道什么是索引吗?

    如果没有,我建议你阅读它。聚集索引是 B 树(平衡树),因此每次插入都必须……等待它……平衡树。因此,集群插入比在堆上插入要慢。如果您不知道什么是堆,那么我建议您在了解基础知识之前停止使用 SQL Server。否则,您正在尝试使用您不知道自己在做什么的产品,基本上是在高速公路上驾驶卡车,蒙上眼睛,以为自己在骑自行车。出乎意料的结果...

    因此,当您在填充表后创建聚集索引时,您的“堆”有一些统计信息可供使用,SQL 基本上可以优化一些事情。这个过程比这复杂得多,但在某些情况下,您会发现事后创建聚集索引可能比简单地插入要慢得多。这与键类型、列数、列类型等有关。不幸的是,这不是一个适合回答的主题,这更像是一门完整的课程,而且本身就很少。查看上面的表格,这是一个非常简单的表格,有大约 7 字节的行。在这种情况下,插入后的 create-index 会更快,但会插入一些 varchar(250) 等,并且球赛会发生变化。

    如果您不知道,聚集索引(如果您的表有)就是您的表。

    希望这会有所帮助。

    【讨论】:

    • 感谢 Ryk - 你的解释非常好。我对聚集索引有一个基本的了解——我只是对 15 倍的性能差异感到惊讶。我预计使用 PK 插入时性能会受到影响,但没有那么严重。听起来我的表的小尺寸放大了当 PK 处于活动状态时插入的相对性能开销。
    • 如果你要投反对票,至少解释一下你的理由——这也是一个很好的阅读stackoverflow.com/questions/4034076/…
    • 否决这个答案,因为我觉得没有必要说“如果你不知道堆是什么,那么我建议在你了解基础知识之前停止使用 SQL Server”。这有点太苛刻了。每个人都是初学者。
    • 如果数据没有按顺序写入,将数据写入没有集群 PK 的表,然后在之后添加它可能会更昂贵。添加集群 PK 时,必须对数据进行物理重组,从而导致 I/O(写入、读取、可能每个页面的写入)比在 PK 已经就位的情况下按顺序写入数据一次多 3 倍。
    猜你喜欢
    • 2021-08-21
    • 2011-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-18
    • 1970-01-01
    • 1970-01-01
    • 2012-10-15
    相关资源
    最近更新 更多