【问题标题】:Is it faster to normalize this table?规范化这张表会更快吗?
【发布时间】:2009-07-15 07:12:12
【问题描述】:

我有两张表,如下所示:

餐桌人:
VARCHAR 名称
整数年龄

表格消息
VARCHAR 消息
VARCHAR 名称

Message 表有数百个插入和删除操作,查询如下:

insert into Message VALUES ('Hello there', 'John');
delete from Message where name = 'John';

我的问题是,是否值得在 People 中添加一个 ID 字段并将 User 作为 Message 中的 id 引用?下面的查询会快很多吗?

FIRST - select id from User where name = 'John'
THEN - delete from Message where id = $id

我最初认为通过搜索 id 进行插入和删除会比通过字符搜索更快。但是快多少呢?速度的提高是否会抵消因需要对 User 表进行额外查询以查找 id 而对性能造成的影响?

【问题讨论】:

  • 如果客户端代码距离数据库服务器很远,那么 SELECT + DELETE 的开销将会大大增加;如果客户端代码和数据库服务器位于同一位置,成本会更高一些。单个语句 - 可能 DELETE FROM Message WHERE Id = (SELECT Id FROM Users WHERE Name = 'John') - 会更有效率。给优化器一些优化的东西,它会为你创造奇迹。把它当作一个愚蠢的文件管理器,它会为你表现不佳。

标签: sql database optimization rdbms


【解决方案1】:

正如你所说,额外的查询会使它稍微变慢(当然这取决于名称的长度、数据库类型等)

但是当用户更改他的名字时会发生什么,当你想删除一个用户时,等等?这种设计会给你带来很多痛苦。不管这个微小的性能问题如何,最好进行标准化,

【讨论】:

  • +1 表示“速度不是一切”,但从长远来看,声音设计确实有回报!
【解决方案2】:

一个人的名字从来都不是一个好的主键,因为名字不是唯一的。它们会随着时间而改变。最好使用代理键(是的,在 Int 上的连接通常更快,并且您可以在许多数据库中加入删除语句而不使用较慢的子查询),特别是因为名称往往比几个字符长。

【讨论】:

  • 你有一点,但是:如果数据库强制执行唯一性,名称可以是唯一的 - 例如这里的“姓名”可能只是他们的登录名,不一定是他们的出生名。该问题没有详细说明架构设计,可能是因为他更关心通过数字与字符串查找的性能。
【解决方案3】:

您的设计已经标准化,假设您对 People.Name 有唯一约束,并且在 Message.Name 和 People.Name 之间存在参照完整性约束。

与其说是规范化问题 - 如果您想允许人们更改他们的姓名,那么您就会遇到性能和可伸缩性问题(需要更新 Message 表中的所有关联行)。

如果人们永远不会在您的系统中更改他们的名字,那么这不是问题。在这种情况下,Name 几乎与 ID 一样好 - 尽管某些 DBMS 可能使用索引数字而不是索引字符串 (?) 表现更好。

删除的性能是另一回事 - 我会说如果你已经有一个唯一的名称,那么按名称删除将比通过 ID 进行查找(或加入)删除要快 - 但你又一次'会想自己做基准测试。

【讨论】:

    【解决方案4】:

    更快吗?然而,只有分析才能说明问题。 . .

    更好的做法是在 Person 上放置一个 id 列,并在 Id 上放置一个从 Message 到 Person 的外键约束(假设所有消息只能发送到 Person 表中的人员)。

    您仍然可以在一个语句中删除消息

    delete from Message where id IN (select id from Person where Name = 'John')
    

    并且数据库将对此进行优化,因此它比两个语句快得多(即比单独的选择和删除语句快)

    您可以在外键约束上指定级联删除,这样当您删除人员时,发送给该人员的所有消息也会自动删除。

    更多信息请参见Foreign Keys

    【讨论】:

    • 我认为“id = (...)”应该是“id IN (...)”,尽管它可能取决于所使用的 SQL 的风格。
    【解决方案5】:

    如果大多数名称都很短(不是 15 到 20 个字符长),并且表的索引正确,那么您将从 id 字段获得的速度性能可以忽略不计。

    【讨论】:

      【解决方案6】:

      您不需要进行额外的查询。你可以这样做:

      DELETE FROM Message 
      INNER JOIN User 
        ON Message.id = User.id 
      WHERE User.name = 'John'
      

      【讨论】:

      • 如果他使用的是 SQL Server,他将需要破解微软漂亮的双 FROM 语法。
      【解决方案7】:

      根据我的经验,网站后端的用户表是几乎 100% 的时间都保留在内存中的表之一。它对于任何活动都非常重要,因此它永远不会超出页面缓冲区。所以我会(并且已经)肯定会使用 userId 来处理所有这样的引用。

      【讨论】:

        【解决方案8】:

        对于您的exact 问题,使用如此小的架构,转储原始 Messages 表的内容,非规范化会更快。查询计划将更小,更容易优化,并且不会有连接开销。

        总的来说,它要复杂得多。

        这样做是否正确是一个问题。为此,从规范化设计开始,但如果有令人信服的理由,愿意并准备好去规范化。非规范化有时有正当的理由,但通常规范化数据的收益会抵消任何性能损失。

        规范化的数据更容易维护并且通常更灵活。为了灵活性,使用数字 pkey 可以让多个人同名。您可以轻松地向人物添加更多字段。无需扫描所有消息即可运行报告以查看系统中的所有人更容易。

        但性能可能是一个因素。鉴于这两个表中的数据,数据库有几个关于如何连接的选项。它可以使用 PeopleMessages 作为基表,连接的完成方式会影响事物(嵌套循环、哈希连接、排序/合并等)。

        但除此之外,归一化实际上可以更快。如果您的架构比您描述的更复杂怎么办?假设您的 People 表有 50 个与 HR 相关的字段,而您的 Messages 表只有一个 20 个字符的消息字段。如果您有两个人但有 100k 条消息的情况,那么非规范化实际上会更快。这是因为 I/O 是数据库的最大限制因素。如果您要在一个查询中转储所有数据,则规范化数据将只获取这 50 个字段一次,并且您的 Messages 表将密密麻麻地塞满数据。在非规范化版本中,Messages 的每一行将包含 51 个字段,并且您将大幅增加 I/O 的数量以获得相同的结果。

        【讨论】:

          【解决方案9】:

          在你有一个好的设计之前,你不应该担心优化。

          目前,我认为很难说这是否是一个不切实际的玩具问题,但通常情况下,您会在设计时考虑到主键和外键关系,而这个键非常 不太可能是 varchar。

          它可能是一个 GUID,也可能是一个 int,但无论哪种方式,您都将至少有一个非聚集索引,也可能是一个聚集索引,并且您将拥有一个非聚集索引。用户名等重要数据的聚集索引,因此最终整个系统的性能(而不仅仅是这个删除)将取决于这些表的可靠规范化设计和良好的索引策略。

          【讨论】:

          • -1 我的大多数查找表在 VARCHAR 列上都有一个 PK,通过 FK 引用。
          • 我的大多数查找表都是 int surrrogates 或 char 列,是的,一些查找表会有 varchar。我坚持我的观点,对于大多数应用程序实体(即没有自然键),您极不可能拥有 varchar 主键。
          • “没有自然键”这个假设太过分了。
          【解决方案10】:

          一切都与 IO 和可维护性有关。如果您的 varchar 包含少于 4 个字节,那么如果您使用 varchar 而不是整数,则查询会更快。但这并不是一个很大的改进,如果您需要更改名称,您会损失很多性能!消息表中的每一行都需要更新(例如删除和插入)。

          如果您使用整数,则仅存储 4 个字节用于对用户表的引用。而且,如果您在用户表中的 ID 和名称上使用覆盖索引,那么这只是一点点开销。用户表可能会保留在缓存中,具体取决于您希望用户表中有多少行以及您拥有多少内存。在这种情况下,您将进行逻辑扫描,而不是慢得多的物理扫描。

          【讨论】:

            【解决方案11】:

            附注:不要忘记在要加入表的列上放置索引,如果它们还没有的话。

            【讨论】:

              猜你喜欢
              • 2011-08-26
              • 1970-01-01
              • 2011-07-17
              • 2010-10-07
              • 2019-08-03
              • 2019-04-21
              • 2018-11-05
              • 2011-02-06
              • 2020-03-01
              相关资源
              最近更新 更多