【问题标题】:Which query will be faster哪个查询会更快
【发布时间】:2023-03-27 21:54:01
【问题描述】:

我有一个包含 17 个字段的用户表和一个包含 40 个字段的用户详细信息表,两者都使用 user_id 连接,这是用户表的主键。它们没有级联 这两个表的行数最多为 400,000 到 500,000 在我的软件中,大多数时候我需要连接这两个表来生成输出。

我的问题是,如果我删除用户详细信息表并将其字段放在用户表本身中,我的查询会运行得更快吗?这样我就可以在不使用连接查询的情况下获得结果

哪个更好 1)一个表中的所有字段或 2) 两个表并使用连接查询

【问题讨论】:

  • 您使用的是什么数据库技术?
  • 什么是类型查询大小?即少数用户或 100,000 个用户。您是否检索了少数几个字段的所有字段?
  • 带有MyISAM的MySQL,用户表不断增加,一年内可以达到40万到50万。最初它由900多个用户组成

标签: mysql database-design


【解决方案1】:

我并不是想成为一个聪明人,但唯一合理的答案是“尝试并自己衡量”。

您看,您的问题中有太多未知变量:

  • 表引擎(MyISAM、InnoDB、...)
  • 其他WHERE 条件(如果有)
  • 额外的JOINs,如果有的话
  • 表结构(如果“40 个字段”都是 INT 或 CHAR 或 TEXT 或 VARCHAR 或某种组合,则它们是非常不同的)。
  • 现有索引
  • 表上的读/写比率,以及相关的喧闹(即使用的锁定模型)
  • 还有很多其他的东西

尝试使用EXPLAIN 运行您的查询/查询;查询计划将为您提供比我们所能提供的更具体的数据。

【讨论】:

  • +1 表示 EXPLAIN,它提供了许多有用的信息 - 尽管您可能需要阅读文档以了解其含义。
  • 条件将出现 3-4 个字段,除此之外还会出现更多连接,引擎是 myisam,几乎 85% 的字段是 varchar,每个 talbe 只有一个主键和没有外键
【解决方案2】:

我建议您使用只有 固定 大小字段的主表。 (没有 TEXT BLOB 字段)。大多数时候这应该足够了。然后,只有在需要这些字段时才加入第二个表。

访问只有固定长度字段的表比使用动态长度行更快。

另一个好的做法是使用最常用的列创建索引。如果所有列都在索引中,那么 MySQL 可以在完全不访问表的情况下为您提供结果(通过仅从索引中读取数据)。

您必须考虑这些选项,但只有了解您对数据库执行的操作的比率才能做出真正的决定。

【讨论】:

  • TEXTBLOB 字段不是分开存储的,表行中只存储了一个(固定大小)指针吗?
  • @Piskvor 我不知道有这样的存储引擎或ROW_FORMAT,但如果你找到一些链接,我很感兴趣。
  • @Piskvor 这取决于。答案可能很复杂。请参阅 mysqlperformanceblog.com/2010/02/09/blob-storage-in-innodb 了解 InnoDB,MyISAM 再次不同。
  • @Isotopp:嗯,这比我想象的要复杂。感谢您的链接!
  • @Piskvor 阅读完这篇文章后,我仍然认为将这些长字段分隔到不同的表中是个好主意。也许更是如此。 :)
【解决方案3】:

您的问题的答案很复杂。如果您的表包含正在写入的字段,如果您的数据是冷的或太大而无法保存在内存中,这将更加复杂。它还依赖于存储引擎、存储引擎版本和其他一些东西。

只有通过基准测试才能获得可靠的结果。

不过,我可能会提供一个有用的轶事。在另一项工作中,我们有一个用户数据库,它在一行中保存单个用户的所有数据。每个用户的用户数据总共约为 1-2K,我们有 2500 万条用户记录。

数据库一直在读取和写入每个 8K 页的数据(MySQL InnoDB 将是 16K 页,顺便说一句)。这意味着我们每个数据库页面大约有 4-6 条用户记录,以及大约 500 万页数据。

用户记录包含存储用户上次登录时间的字段。在早上 7 点到 9 点之间,我们将看到大约 800 万唯一用户登录,因此我们将有大约 800 万页被污染并需要回写。本质上,我们每天会将整个用户表写回磁盘两次或三次。

我们引入了一种人为的 1:1 关系,类似于您的用户和 user_details 页面:我们有一个用户和一个 user_lastlogin 表。 user_lastlogin 记录非常狭窄,基本上只包含用户 ID 和用户的(三个不同的)最后登录时间(取决于所使用的服务)。因为 user_lastlogin 非常窄,我们现在每页有近千条记录,而表中只有 25.000 页。有了 800 万个唯一登录,我们现在需要写回更少的数据(因为数据库中的检查点和页面写回被延迟)。磁盘 I/O 负载下降了几个数量级。

经验教训:将静态数据与易失数据分开可能非常有用(我们考虑了密码静态数据,因为我们每天有大约 15000 次密码更改,而我们每天有 800 万次 last_login 更新)。

【讨论】:

    【解决方案4】:

    最好将所有数据放在一个表中。否则,您正在违反数据库规范化规则。为什么要在 2 个表中分离一个用户的数据?只需通过不将通配符选择器用作 * 来限制您的 SELECT 结果,而是使用请求的字段创建一个列表。

    【讨论】:

    • 效率或现实世界的要求,也许?一个表可以是系统内部使用的假名标识符,而详细信息表可能是 a) 相对较少需要的数据(您不需要用户 #19746 的邮寄地址或星号来查看他是否被允许访问资源#0xdeadbeef;此外,标识符通常是整数,比 VARCHAR 或 TEXT 处理起来要快得多 - 表宽度和组成对速度有实际影响)或 b)所有进程都不能访问的数据(想到 HIPAA) .
    • 我同意这一点,但这种情况并不经常发生。我仍然更喜欢将所有内容都保存在一个表中,除非我真的能看到将数据分离到更多表中的好处。如果他想限制某些用户或应用程序部分的可见数据,他可以使用视图。有很多用例,我说的是最常见的一个,因为他没有提到任何具体的要求。
    • 至于标准化,一旦您的客户有了邮寄地址、帐单地址和送货地址(最常见的示例),that 就会对您产生影响。那里的规范化方法绝对不是“将它们全部塞入表格中的一行”。
    • 我也同意这一点 :) 但要建议分离,我需要查看有关用户数据的更多信息。
    • @Yasen Zhelev:现在我们达成了一致 :) 这就是为什么我反对您的无条件“最好将所有数据放在一个表中”。如果不了解具体数据,我们只是不知道。
    【解决方案5】:

    联接是一项相对昂贵的操作,因此如果您可以删除它们可能会加快速度。不过,这是以标准化为代价的。

    您可以通过脚本生成随机用户记录吗?如果是这样,您可以使用两种模式创建 500,000 条记录(全部在一个表和两个带连接的表中),看看哪个最快。可能是差异太小,没有必要合并表。

    【讨论】:

    • 强调“相对”。一个包含 0.5M 记录的 57 列的表可能不会很快,尤其是。如果涉及字符串。 “将所有内容加入所有内容”确实很慢,但“删除连接会加快速度”是一种过度概括。正如您正确指出的那样,它可能可能不会
    • “不过,这是以标准化为代价的。”这是以人工身份证号码为代价的。存储自然键而不是人工 id 号可以消除连接,而完全不影响规范化。不过,这可能会也可能不会加快速度。
    猜你喜欢
    • 2012-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-16
    • 1970-01-01
    • 1970-01-01
    • 2012-03-22
    • 1970-01-01
    相关资源
    最近更新 更多