【问题标题】:Can I expect a performance gain from removing this JOIN?删除此 JOIN 是否可以获得性能提升?
【发布时间】:2011-02-05 13:06:24
【问题描述】:

我有一个包含 100 万行的“items”表和一个包含 20,000 行的“users”表。当我从“items”表中选择时,我会在“users”表上进行连接(items.user_id = user.id),这样我就可以从 users 表中获取“用户名”。

我正在考虑向 items 表添加用户名列并删除连接。我可以期望从中获得可观的性能提升吗?它已经非常快了,但是减少我的负载(非常高)会很好。

不利的一面是,如果用户更改了他们的用户名,项目仍然会反映他们的旧用户名,但如果我可以期待一个可观的性能提升,这对我来说是可以的。

我之所以问 stackoverflow,是因为基准测试并没有告诉我太多。两个查询都很快完成。无论如何,我想知道删除连接是否会在很大程度上减轻数据库的负载。

连接查询示例:
选择Item.idItem.submitter_idItem.source_imageItem.cached_imageItem.Item,@987654331.@987654331.@9876 987654333 @。widthItemheightItemstatusItempopularItemmade_popularItemfave_countItem .tags, Item.user_art, Item.nudity, Item.created, Item.modified, @9876454355.,removed,@9876 987654358 @,ItemtestItemrecsItemrecs_dataUseridUserusernameUserpassword ,User.email,User.fullname,User.profileurl,User.homepage,User.bio,User,@98765438@9866 987654383 @。avatarUserff_userUserff_keyUserff_last_faveidUsertwitter_userUsertwitter_passUser .emailalerts, User.showunsafe, User.@98765 4400 @,User 987654402 @,User 987654404 @,User 987654406 @,User 987654409 User 987654411 @。@ 987654412 UserUser 987654414 @,User 987654416 @,User items Item least join users User submitter_id 987654424 submitter_id 987654424 = User.id) 其中Item.nofront != 1 AND Item.removed != 1 AND Item.made_popular 不为 NULL AND @nudity != 1 ORDER BY 987654433@.made_popular DESC LIMIT 1040, 290;

不带连接的示例查询:
选择Item.idItem.submitter_idItem.source_imageItem.cached_imageItem.@98765444@,source_urlsource_url,@9866 987654447 @。widthItemheightItemstatusItempopularItemmade_popularItemfave_countItem .tags, Item.user_art, Item.nudity, Item.created, Item.modified, Item,@9876544769@,@9876 987654472 @,Item 987654474 @,Item 987654476 recsItem 987654479 items AS Item 987654481 Item!= 1和Itemremoved != 1 AND Item.made_popular is not NULL AND nudity != 1 ORDER BY Item.made_popular DESC LIMIT 1040, 290;

【问题讨论】:

  • 当您选择 where 子句中的内容时?
  • 将两个查询添加到上面的帖子中。
  • 您可以为上述查询发布一个解释计划吗?看起来您正在从用户表中检索大量数据。你需要这一切吗?如果您使用 id 和 name 索引用户表,则使用连接检索 user_name 应该非常快。 MySQL 也应该很容易有效地缓存表。我希望删除每行返回的额外用户数据比删除用户表上的快速索引查找带来更大的好处。
  • 雅各布泰勒,说得好。我从查询中删除了大部分用户数据和大量项目数据。我很好奇。为什么检索更少的数据会带来如此巨大的好处?是 php 问题(更大的对象 = 更多内存)还是会显着影响查询时间?好处是否足够大,以至于人们可能希望尽可能避免添加另一个表列,或者这不是什么大不了的事?
  • @makeee,如果用户名被编入索引,您可以进行连接,而无需接触真实的表格。仅获取用户名的连接可以从索引中获取该信息。这可能是最大的改进。较小的改进是“通过网络”传输更少的数据,这在网络环境中很重要,但即使对于本地数据库也可能很明显。

标签: mysql join


【解决方案1】:

我有一个包含 100 万行的“items”表和一个包含 20,000 行的“users”表。

也就是说,无论您是 JOIN 还是非规范化,您仍然会通过网络传输大约 1M/20k = 50 倍以上的 User 信息,而不是严格必要的。编码、传输和解码数据会增加负载。

我正在考虑向 items 表添加用户名列并删除连接。

如果您只需要用户名,为什么还要在您原来的JOIN 中提供所有其他(可能是大量的)信息(例如User.profileurlUser.homepage 等)?请记住,对于User 列,您平均传输每个位信息的 50 个副本。您是否考虑过从JOIN 中大幅削减您在SELECTing 中的列(来自UserItem 表?)

我之所以问 stackoverflow,是因为基准测试并没有告诉我太多。两个查询都很快完成。无论如何,我想知道删除连接是否会在很大程度上减轻数据库的负载。

在第一阶段,删除您不打算使用的列可以减少负载,因为需要编码、传输(从服务器到客户端应用程序)然后解码的数据更少。

在第二阶段,让我从我自己的一个问题开始:您真的需要一次性完成所有百万行吗?如果您不需要,例如如果您是用户界面驱动的并且您对它们进行分页(使用OFFSET ... LIMIT ...),那么您不一定会关心50x User 信息重复(除非LIMIT 达到数万。)否则,您可能想要衡量通过首先将SELECTing User.idUser.username 放入应用程序内存(20k对,放入哈希表/映射),然后 SELECTing only Item 行(1M 次迭代)每次在应用程序级别解析 Item.user_id 针对哈希表/映射.

当然,始终使用EXPLAIN 以确保正确的索引存在并且在应该使用索引时使用,并在任何表从几百行以下增长到数千或数百万行后运行ANALYZE TABLE .

【讨论】:

  • 您是正确的关于有额外的用户信息。我没有意识到这有什么大不了的,但回想起来还是有道理的。我将从削减它开始。
【解决方案2】:

正确的答案是测量它,在目标环境中,看看它是否有所作为。然后进行成本/收益分析,看看是否值得。

成本是增加的存储空间和数据不同步的可能性(但请参阅下文了解如何缓解这种情况)。好处是提高速度或减少负载。

数据库模式不是一劳永逸的操作,它们应该随着基础数据的变化而定期调整。这就是 DBA 的报酬,持续监控和调优。

在任何情况下,通过使用触发器,在体面的 DBMS 中可以很容易地控制列的复制。我的意思是在 users 表上放置一个插入/更新触发器,这样,如果用户更改了他们的用户名,它也会在 items 表中更改(反之亦然)。

MySQL 是否符合我对一个体面的 DBMS 的定义,我无法评论 - 我自己就是一个 DB2 开发者。但是从第三范式恢复是一种久经考验的技术,可以从数据库中榨取最后一盎司的性能,并且只要您了解后果,这是完全可以接受的。很少有人抱怨他们的数据库占用了太多的磁盘空间。 许多抱怨他们的查询运行速度有多慢。

请记住,当您遇到性能问题时, 是您要做的事情。这不是仅仅因为你认为它可以减少负载就应该做的事情。除非负载(或花费的时间)实际上是一个问题,否则您的成本/收益分析的收益部分为零,因此任何理智的 bean 计数器都会告诉您这意味着“没有变化”。


根据您添加的查询,我有几点要说明:

  • 首先是nudity 列。请告诉我如何访问该数据库:-)
  • 您应该提取您需要的列。如果用户名是您从 User 表中需要的全部内容,那么您不应该在第一个查询中获得所有额外的内容。 Item 的东西可能同样如此 - 只得到你需要的东西。
  • 确保您对WHERE 子句中使用的所有列都有索引 - 这也可能需要组合索引(具有多于一列的索引)。索引的内容取决于您的查询,但WHERE 子句中使用的每一列都是分析的良好开端。
  • 对于大型表,您可以考虑定期将已删除的项目“清扫”到单独的表中(例如,RemovedItems)以最小化Items 的大小并加快查询速度。但请记住,这仅在您很少需要查找已移动项目时才有用,因为它会使这些查询复杂化(通过强制它们在两个表中搜索而不是一个表)。同样,这是一个成本/收益的事情。一百万行并不是一张真正的大表(至少在我的世界里)。

【讨论】:

  • 感谢您的建议。关于只提取我需要的列的好点。我正在检查我所有的查询,以确保我只得到我需要的东西。我已经确保我所有的索引都很好。 “清扫”是指删除列吗?我听说最好把它们留在里面..
  • “清扫”是指删除(或移动到“归档”表中)不再活动的行或您希望访问频率低于其他行(分区)。
  • 我不需要“删除”的行,但我记得听说删除行会减慢查找速度/导致其他问题。这不是真的吗?
  • 如果您在某些时候需要数据,删除行可能会导致问题,但这里的情况似乎并非如此。删除过程可能非常耗时,以至于 Oracle 引入了软删除功能 - 在他们的实现中,该行被标记为已删除,但实际上并未从表中物理删除(与您正在执行的操作相同,但作为 DBMS 本身的一部分)所以没有办法获取数据)。然后,它允许您在安静的时间(例如,午夜)以物理方式删除所有逻辑删除的记录。我建议您也可以在安静的时间进行清扫。
  • paxdiablo:感谢您提供的信息。物品很少被移除。除了必须通过的额外行(可能不多)之外,在我的查询中使用“removed = 0”会降低性能吗?如果唯一的问题是查询必须跳过几百行额外的行,那么似乎不值得进行任何清理。
【解决方案3】:

我建议您保持这种方式以保留规范化表。我认为将用户名放在项目表上不是一个好主意,因为它会使数据变得多余。您是否尝试过重新索引您的表格?

【讨论】:

    【解决方案4】:

    JOINS 总是比简单的 SELECT 语句占用更多的资源。所以是的,删除 JOIN 应该会提高性能。

    【讨论】:

      【解决方案5】:

      如果您在items.user_iduser.id 上缺少索引,或者您使用的是蹩脚的数据库,您只会看到显着的性能提升。否则,性能不会显着提高。

      【讨论】:

        猜你喜欢
        • 2012-03-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-06
        • 2019-12-29
        • 2011-07-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多