【问题标题】:Uniqueidentifier PK: Is a SQL Server heap the right choice?Uniqueidentifier PK:SQL Server 堆是正确的选择吗?
【发布时间】:2010-11-20 04:11:27
【问题描述】:

好的。我已经在这里和那里阅读了有关 SQL Server 堆的内容,但没有什么太确定而无法真正指导我。我将尝试衡量性能,但希望对我应该研究的内容提供一些指导。这是 SQL Server 2008 企业版。以下是表格:

工作

  • JobID(PK、GUID、外部生成)
  • 开始日期(日期时间2)
  • 账户ID
  • 更多会计字段,主要是小数和大整数

工作步骤

  • JobStepID(PK、GUID、外部生成)
  • JobID FK
  • 开始日期
  • 更多会计字段,主要是小数和大整数

用法:大量插入(数百次/秒),通常每个作业 1 个 JobStep。估计每月可能有 100-200M 行。根本没有更新,唯一的删除来自归档超过 3 个月的数据。

对数据执行约 10 次查询/秒。有的加入 JobSteps 到 Jobs,有的只看 Jobs。几乎所有查询都将在 StartDate 上进行,其中大多数包括 AccountId 和其他一些会计字段(我们对它们有索引)。查询非常简单 - 执行计划的最大部分是 JobSteps 的连接。

优先级是插入性能。数据出现在查询中的延迟(5 分钟左右)是可以容忍的,因此复制到其他服务器并在它们之外运行查询当然是允许的。

除了将 JobSteps 加入到 Jobs 之外,基于 GUID 的查找非常少见。

当前设置:没有聚集索引。唯一看起来像候选人的是 StartDate。但是,它并没有完美地增加。可以在 StartDate 之后的 3 小时窗口内的任何位置插入作业。这可能意味着以非最终顺序插入一百万行。

1 个 Job + 1 个 JobStepId 的数据大小,使用我当前的索引,大约是 500 字节。

问题:

  • 这对堆有用吗?

  • 集群对 StartDate 的影响是什么,当它在大约 2 小时/100 万行中几乎没有顺序时?我的猜测是不断的重新排序会杀死插入性能。

  • 我是否应该只添加 bigint PK 来获得更小的、总是增加的键? (我仍然需要用于查找的指南。)

我读过GUIDs as PRIMARY KEYs and/or the clustering key,它似乎暗示即使发明一个键也可以在其他索引上节省大量空间。还有一些资源表明堆通常存在某种性能问题,但我不确定这是否仍然适用于 SQL 2008。

再一次,是的,我将尝试执行测试和测量。我只是想获得一些指导或指向其他文章的链接,以便我可以就要考虑的路径做出更明智的决定。

【问题讨论】:

    标签: sql sql-server performance sql-server-2008 indexing


    【解决方案1】:

    是的,堆有问题。您的数据将在整个节目中逻辑碎片化,不能简单地进行碎片整理。

    想象一下,把你所有的电话簿都扔进一个桶里,然后试图找到“bob smith”。或者使用对姓氏、名字有聚集索引的传统电话簿。

    维护索引的开销是微不足道的。

    StartDate 除非是唯一的,否则不是一个好的选择。聚集索引需要非聚集索引的内部唯一性。如果未声明唯一,SQL Server 将添加一个 4 字节的“唯一性”。

    是的,我会使用 int 或 bigint 来简化它。至于 GUID:请参阅屏幕右侧的问题。

    编辑:

    注意,PK 和聚集索引是 2 个独立的问题,即使默认 SQL Server 会使 PK 聚集。

    【讨论】:

    • 是的,正如所有其他问题所显示的那样,我绝对希望避免将 GUID 作为集群键。 StartDate 需要一个额外的身份。我只是担心在 2 小时内插入基本上“随机”的开始日期可能意味着大量的重新排序或其他事情。所以,简而言之,去添加一个 bigint PK 让它变得漂亮和集群?
    • @MichaelGG:是的。它是窄的、数字的、严格单调递增的、唯一的 = 好的聚集索引
    • 对,我知道 int PK 比 guid 更适合。我的问题是添加一个新的 bigint 列是否只是为了拥有聚集索引,而不是将其保留为堆是一个好主意。看来这是正确的举动。
    【解决方案2】:

    堆碎片不一定是世界末日。听起来您很少会扫描数据,所以这不是世界末日。

    您的非聚集索引会影响您的性能。每个都需要将行的地址存储在底层表(堆或聚集索引)中。理想情况下,您的查询永远不必使用基础表本身,因为它以理想的方式存储所需的所有信息(包括所有列,因此它是一个覆盖索引)。

    是的,Kimberly Tripp 的东西是最好的索引。

    罗伯

    【讨论】:

    • 一些查询是聚合的,可以单独使用索引提供服务。但是许多人需要从两个表中返回几乎每一列。我是否了解 Kimberly 的信息,即通过从 guid 切换到集群 bigint PK,我将在其他索引上节省大量空间?
    • 更改结构(或聚集索引中的内容)将影响表中的每个非聚集索引。我建议将您的数据保留为堆将是要走的路。花时间让您的非聚集索引正确。实际上,您甚至可能会发现包含许多列的非聚集索引仍然比获得正确的聚集索引更好。但是,是的,bigint(8 字节)小于 guid(16 字节),并且出现在 NCIX 的每个叶行上。
    • 另外...由于您不更新,将其保留为堆和放置代理键之间的最大区别是: 1. 引入一个新字段将增加每一行,给您碎片化您不需要。 2. 你的表需要重建,NCIXs 也需要重建。 3. NCIX 会稍微小一些,因为“行地址”会缩小一点。但归根结底,让您的 NCIX 正确,您不必关心底层堆/CIX 的样子。
    • 可能会影响您的决定的事情 - 您是否已经有数据涌入您的堆中,或者您正在设计这个空的?
    • 堆中有数据,但很小,我们正计划升级系统。因此,我们有能力将其离线并根据需要重新设计。
    【解决方案3】:

    正如您自己的研究和所有其他回答者所提到的,使用 GUID 作为表上的聚集索引是个坏主意。

    但是,拥有堆也不是一个好的选择,因为堆还有其他问题,主要与碎片和其他无法与堆一起使用的问题有关。

    我的最佳实践建议总是这样:

    • 请在任何数据表上使用主键、聚集键(除非它是临时表或用于批量加载的表)
    • 尝试确保集群键是 INT IDENTITY 或 BIGINT IDENTITY

    我认为通过添加 INT/BIGINT 获得的好处——即使只是为了拥有一个好的聚集索引——也远远超过了它的缺点(正如 Kim Tripp 在你引用的她的博客文章中所说的那样)。

    马克

    【讨论】:

      【解决方案4】:

      由于 GUId 是您的主键和外键,您的数据库仍需要检查每个插入的约束,您可能需要为其编制索引。不建议对 GUId 进行索引,因为它具有随机性。因此,我绝对要说,您应该为您的主键使用 bigint(可能是标识)路线并将其用作聚集索引。

      【讨论】:

      • 我仍然需要对 guid 进行索引,因为我需要偶尔对其进行查找。填充因子较低的非聚集索引应该可以正常工作,不是吗?
      • 我会根据您需要进行“偶尔”查找的频率进行调用。如果您说插入速度是关键,那么更少的索引会有所帮助,尤其是像 GUId 这样的复杂索引。如果您每周进行一次 GUId 查找,那么与每秒保持 100 次插入的索引相比,慢速表扫描可能是可以接受的……不过,我建议您对此进行分析以确保这一点。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-08
      • 1970-01-01
      • 2021-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多