【问题标题】:Should SQL Server transaction tables always have a surrogate primary keySQL Server 事务表是否应该始终具有代理主键
【发布时间】:2011-06-27 13:42:56
【问题描述】:

对于已经具有主键(4 列的自然复合键)的大型事务表(1 亿行,20 GB),添加标识列并将其作为主键是否有助于提高性能?

当前主键(4 列的自然复合主键)可以完成这项工作,但有人告诉我,您应该始终有一个代理键。 那么,是否可以通过创建标识列并将其作为主键来提高性能?

我使用的是 SQL Server 2008 R2 数据库。

编辑:此事务表主要连接到定义表并用于填充报告。

编辑:如果我确实添加了代理键,它将不会用于任何连接。将使用现有的关键字段。

编辑:该表不会有子表

【问题讨论】:

  • 这是一个“取决于”类型的问题。加载交易数据后,您在做什么?您是否需要根据元数据或其他事务表连接各个行?还是您主要使用汇总形式的交易数据?您在其他表中是否有相关的行?标识列实际上只会在连接中有所帮助 - 它会在较小程度上降低您对表和空间的写入性能。
  • @Aaron: +1 - “身份列实际上只有助于连接”。
  • @Aaron:感谢您的评论。我已经更新了我的问题。
  • @Yuck:另一个潜在的用例是,如果记录事务的应用程序要在其他地方记录一些东西——它可以使用 SCOPE_IDENTITY() 值而不是更广泛的自然键,这将在如果有调查结果,则可以更轻松地返回并识别有问题的行。但就其本身而言,这似乎不是那种会倾斜天平以支持添加代理的事情。
  • @Aaron:默认情况下,我将所有表设计为使用 intbigint IDENTITY 列作为具有聚集索引的代理主键。已经写了很多文章来支持这一点。当然有这样做的原因,但对于大多数 OLTP 解决方案来说,这是一个合理的起点。

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


【解决方案1】:

仅仅添加一个 IDENTITY 列并为其添加一个新的约束和索引不太可能提高性能。该表将更大,因此扫描和查找可能需要更长的时间。还将有更多索引要更新。当然,这一切都取决于您测量的性能...以及您是否打算在添加新列时对代码或数据库进行其他更改。添加一个 IDENTITY 列而什么都不做可能是不明智的。

【讨论】:

    【解决方案2】:

    仅当:

    • 您有更大的子表
    • 您有非聚集索引

    在每种情况下,您的表的 PK(假定为集群)将位于每个子条目/NC 条目中。因此,使聚集键更窄会受益。

    如果您只有非 NC 索引(可能有一个)并且没有子表,那么您将获得的只是

    • 更宽的行(使用更多的数据页)
    • 略小的 B 树(占总空间的一小部分)

    ...但是您仍然需要对当前 4 列进行索引/约束 = 增加空间。

    如果您的 4 路键也捕获父表键(听起来很可能),那么您将失去重叠的优势。不过,这将被新的索引/约束覆盖。

    所以不,你可能不想这样做。

    我们丢弃了十亿多行表上的代理键 (bigint) 并移至实际的 11 路键并由于更简单的结构(少一个索引,每页的行数略多)减少了 65% 以上的磁盘空间等)

    【讨论】:

    • 谢谢。十亿以上行表上的信息很有帮助。
    【解决方案3】:

    考虑到您的编辑,以及尽管问题引发的所有讨论,我建议在此表中添加 IDENTITY 列弊大于利。

    【讨论】:

      【解决方案4】:

      性能受损的一个地方是自然键中数据的更改。然后必须将更改发布到所有子记录。例如,假设其中一个字段是公司名称,而公司更改了名称,那么所有相关记录(可能有数百万条记录)都必须更改,但如果您使用代理键,则只需更改一条记录改变。整数联接往往更快(通常比 4 列联接快得多),并且编写要联接的代码通常也更快。但是,另一方面,拥有至关重要的四个字段可能意味着不需要经常进行连接。插入性能会受到轻微影响,并且必须生成和索引代理键。通常这是一个很小的打击,以至于不引人注意,但这种可能性是存在的。

      四列自然键通常并不像您想象的那样是唯一的,因为数据的列数往往会随着时间而变化。虽然它现在是独一无二的,但随着时间的推移它会变得独一无二吗?如果您在自然键上使用了代理键和唯一索引,但后来证明它不是唯一的,那么您所要做的就是删除唯一索引。如果是 PK 并且有子表,你必须完全重新设计你的数据库。

      只有您可以决定如果这些考虑因素中的任何一个影响您的特定数据需求,代理键对某些应用程序更好,而对其他应用程序则更差。

      【讨论】:

        【解决方案5】:

        ---编辑: 根据对问题的修改,添加身份/代理键可能无法解决此问题。

        --原始答案。

        当您使用连接并且您有子表时,性能改进的一种情况。

        在没有代理键的情况下,您必须将所有 th4 4 键复制到子表并连接 4 列。

        t_parent
        -------------
        col1,
        col2,
        col3,
        col4,
        col5,
        constraint pk_t_parent primary key (col1,col2,col3,col4)
        
        t_child
        ----------
        col1,
        col2,
        col3,
        col4,
        col7,
        col8,
        constraint pk_t_child primary key (col1,col2,col3,col4, col5),
        constraint fk_parent_child foreign key (col1, col2, col3, col4) references
                                         t_parent ((col1, col2, col3, col4))
        

        连接将包括所有 4 列..

        select t2.*
          from t_parent t1, t_child t2
          where (t1.col1 = t2.col1 and 
                 t1.col2 = t2.col2 and 
                 t1.col3 = t2.col3 and 
                 t1.col4 = t2.col4
                )
        

        如果您使用代理键并在 4 列(现在是主键的一部分)上创建唯一约束,它将既高效又像以前一样验证数据。

        【讨论】:

        • 虽然确实如此,但我发现您需要的信息通常都包含在复合键中。发生这种情况时,您首先不需要连接 - 您需要的信息已经在引用表中。 (这种情况比我想象的要频繁得多。)
        • 我同意。但是,如果您有 3-4 级深的子表/从属表,则层次结构中表的主键将包含过多的列而不是必要的。如果不是马上,可能会在某个时候加入。最重要的是,我认为使用代理键而不是在需要时(或稍后可能需要连接时)更改表设计是一致的
        • 我希望 100m 行表是最低级别。您的情况没有加起来:增加此表的大小以节省可能不存在的子表中的空间,这将是一个非常特殊的情况(例如非常窄或仅用于子集)并且100m 父表的大小增加将抵消子表的好处。更新:OP 现在说没有子表
        • @gbn- 我不是为了节省子表的空间而提议这样做的。可能 1 亿行是最低级别的表,它永远不需要更多的子表,但它不是一个任意的数字来做出这个决定吗?在其他情况下,我们如何确定哪一个是最低级别的表?
        • @Rajesh Charmarthi:如果我引用了 4 级深的表,我希望每个级别的表的主键都具有所需的列数。 CREATE VIEW 是你的朋友。
        猜你喜欢
        • 2010-10-24
        • 2011-05-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多