【问题标题】:Multiple SELECTs vs Single Query with JOIN多个 SELECT 与使用 JOIN 的单个查询
【发布时间】:2015-06-03 18:22:12
【问题描述】:

我们当前的设置看起来有点像这样。

public_entry(5.000.000 行)→telephone_number(5.000.000 行)→ user(400.000 行)

3 个表,右侧的箭头表示外键约束,其中包含来自右表的外键(整数)。

现在我们有两个想要在网络应用中呈现的数据的“视图”。

  1. 根据用户属性显示带有公共条目的电话号码(例如,仅来自男性用户的号码),有点像分数。
  2. 根据输入日期显示带有公开条目的电话号码

无论号码是否符合您的需求,每个结果都应获得一个分数(例如,您正在寻找水管工,如果该号码在您所在的地区并且相关用户是水管工,则电话号码应该得分高)。

我们尝试了几种方法来解决这个问题,有两种情况。

第一种方法在表上执行带有 INNER JOIN 的 SELECT,如下所示

SELECT ..., (...) as score
    FROM public_entry pe
    INNER JOIN telephone_numer tn ON tn.id = pe.numberid
    INNER JOIN user u ON u.id = tn.userid WHERE ... ORDER BY score

在较小的系统上使用此查询,1/4 的生产系统性能非常好,即使在负载下也是如此。 然而,当我们将这个查询放到生产系统中时,它的执行时间超过了 30 秒。

第二种方法是使用 public_entry 上的单个 SELECT 过滤所有 public_entries,而不使用任何 JOIN,并对其进行迭代,为每个 public_entry 调用一个 SELECT 获取电话号码和用户,计算分数并丢弃结果,如果电话号码和用户没有匹配我们的过滤器/兴趣。

通常不会考虑第二种方法,因为它会为单个页面加载创建超过 300 个查询。 Foreach 处理结果并在 foreach 中调用 SELECT 通常被认为是不好的风格。

但是方法二在生产系统上执行。不太好,但不会花费更多 tahn 1-3 秒,而且在测试系统上也表现不佳。

您对问题所在有什么建议吗?

编辑:

查询

SELECT COUNT(p.id)
    FROM public_entry p, fon f, user u
    WHERE p.isweb = 1
      AND f.hidden = 0
      AND f.deleted = 0
      AND f.id = p.fonid
      AND u.id = f.userid
      AND u.gender = "female"

这个查询有 3 秒的执行时间。

这只是一个示例查询。我可以取出哪里,它的表现会差一点。一般来说,如果我们对数据执行一个带有单个 INNER JOIN 的 SELECT COUNT(),查询会爆炸(30 秒)

【问题讨论】:

  • 很难说没有看到您正在运行的实际查询以及解释、表架构信息、所需的查询结果等。我怀疑嵌套查询是否是一个好的解决方案。
  • @MikeBrant 我会尝试添加更多信息
  • 一些真正有用的信息是查询的解释计划。通过在其前面加上 DESC 来描述查询,例如DESC SELECT ...。可能的罪魁祸首是您的查询缺少覆盖索引。通过该解释计划,我们可以建议可能有帮助的索引。
  • 性能很大程度上取决于 (1) WHERE 子句和 (2) 索引。请告诉我们两个。在您的特定情况下,ORDER BY 不能用于优化。
  • @RickJames 我将在接下来的几个小时内添加信息。

标签: php mysql join


【解决方案1】:

我没有你想要的神奇答案,但这里有一些性能不佳的“原因”,以及一些可能的解决方法(带有警告)。

iswebhiddendeletedgender 中哪一个最“有选择性”?该优化器认为它们无用且烦人。也就是说,如果每个都有两个值并且该字段上的 INDEX 可能是无用的。因此,它选择一个表,进行完整扫描,然后进入下一个表,等等。请注意,在 EXPLAIN 中,它首先选择了最小的表 (user)。当WHERE 子句看起来没有任何用处时,优化器通常会这样做。

无论是 MySQL 完成所有这些工作,还是您完成所有这些工作,都需要付出相同的努力。也许您可以更快地做到这一点,因为您可以在内存中拥有一个简单的关联数组,而 MySQL 被编码为允许表存在于磁盘上,并逐块“缓存”在 RAM 中。但是,如果您没有足够的 RAM 来加载所有内容,那么您将无法使用 MySQL。

如果你真的删除了“隐藏”和“删除”的行,任务会快一点。

您的两个 SELECT 看起来不太相似。你是在暗示选择范围很广吗?而且您实际上需要查看所有 3 个表中的大部分来获得“分数”或“计数”?

让我们从数据仓库的方法来看……一些数据是“静态的”吗?也就是说,不变的,可以概括?如果是这样,将小计 (COUNT(*)) 预先计算到汇总表中将使最终查询速度更快。 DW 通常涉及按天计算的小计。但它要求这些小计不要改变。

COUNT(x) 具有检查x 是否为NULL 的开销。通常这不是必需的,COUNT(*) 会给你你想要的。

您多久运行一次相同的 SELECT?或者,至少,类似的选择?您需要最新的分数吗?我正在寻找在半夜运行所有可能的查询,然后使用结果 24 小时。请注意,通过一次执行多项操作,某些查询可以运行得更快。例如,不要对“女性”和“男性”进行两个 SELECT,而是执行一个 SELECT 和 GROUP BY gender

【讨论】:

  • 当用户添加新条目时,大多数结果每小时都会发生变化。通常在位置表上有一个 4th JOIN,只显示给定区域的结果,因此我们可以尝试缓存每个区域的结果。问题是,我们必须事后计算分数。这种缓存技术不适用于按分数排序的视图,因为这些视图中的每一个都是针对用户需要的并且具有太多的不变量,所以我们基本上最终会缓存太多不同的结果集,只是为了快速处理 1 个特定的用户。
猜你喜欢
  • 2010-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-28
  • 2014-03-13
  • 2020-02-26
  • 2013-06-11
  • 2010-09-27
相关资源
最近更新 更多