过滤查询大约需要 50 秒才能加载

Filtering query takes around 50 seconds to load

我有一个页面,用户可以在其中使用非常广泛的过滤器集搜索职位空缺。其中两个过滤器是复选框,用户可以在其中 select 兴趣和可访问性。当 if selected 时,至少其中一个必须出现在某个空位上才能作为结果显示。

我正在使用 CodeIgniter PHP 框架。 使我的网站崩溃的生成查询示例如下所示:

SELECT v.*
     , o.name as orgname
     , GROUP_CONCAT(DISTINCT(vi.interest_id)) interests
     , GROUP_CONCAT(DISTINCT(i.description)) interestnames
     , GROUP_CONCAT(DISTINCT(i.name_brown)) interesticons
     , GROUP_CONCAT(DISTINCT(e.engagement_key)) engagement_key
  FROM vacancies v
  LEFT 
  JOIN organization o 
    ON v.org_id = o.org_id
  LEFT 
  JOIN vacancy_interests vi 
    ON v.vacancy_id = vi.vacancy_id
  LEFT 
  JOIN interests i 
    ON vi.interest_id = i.interest_id
  LEFT 
  JOIN engagement e 
    ON v.engagement = e.engagement_id
  LEFT 
  JOIN cities_be c 
    ON v.address_city_id = c.cities_be_id
  LEFT 
  JOIN vacancy_accessibility va  
    ON v.vacancy_id = va.vacancy_id
 WHERE v.is_deleted != 1
   AND v.status = 1
 GROUP 
    BY v.vacancy_id
HAVING MAX(vi.interest_id IN (3,4,6,7,9,10)) > 0 
   AND MAX(va.accessibility_id IN (2,1,3,4,5,6)) > 0 
 ORDER 
    BY v.modified_time DESC
 LIMIT 18

我在我的网站上使用的每个过滤器都很顺利并给我结果,但是 vacancy_accessibility table 使我的搜索页面在使用时完全崩溃。在我有大约 100 个职位空缺的本地主机上,它运行良好且快速。但是我的生产服务器,我有大约 11,000 个空缺,一切都变得糟糕,搜索需要大约 5-6 分钟才能完成。

我已经执行了上面粘贴的 MySQL workbench 中的查询,查询计算大约需要 52 秒。

如果我执行相同的查询,但只使用 selected 兴趣(这意味着我不通过 LEFT JOIN 加载可访问性 table,它工作得非常快(大约 0.1 秒). 这实际上并没有反过来工作,我已经尝试删除 interest left join 并且查询速度更快,但它仍然是大约 16 秒。请注意,我确实需要加入兴趣,所以这不是一个真正的选择,只是为了调试。

空缺可及性table实际上与vacancy_interests完全相同。它包含 vacancy_id 列和 accessibility_id 的参考列。空缺兴趣 table 包含 28k 行,而空缺可及性 table 仅包含 5.8k 行... 我确实注意到在可访问性 table 中没有设置 FK,而在兴趣 table 中设置了它们。我不确定这是否会导致如此大的延迟(实际上并没有那么多行)。

有人看到我的查询有什么问题导致我的页面崩溃吗?

您当前的Select存在几个问题:

  • 它必须先进行所有连接,然后在开始过滤之前进行聚合
  • 这些连接增加了行数,因为有些是 m-n 而不是 n-1,最终导致 GROUP_CONCAT(DISTINCT)
  • 所有这些连接都是外部连接,从不减少行数

尝试尽快应用过滤器和 GROUP_CONCATs 并尽可能切换到内部联接。将这些规则应用于查询的第一部分:

SELECT `v`.*, 
   vi.interests, 
   vi.interestnames, 
   vi.interesticons

FROM `vacancies` AS `v`
JOIN 
 ( -- Derived Table to apply filter & concat as soon as possible
   -- also results in a single row per vacancy_id (n.1-join)
   SELECT `vacancy_id`
     ,GROUP_CONCAT(i.interest_id) AS interests
     ,group_concat(i.description) AS interestnames -- DISTINCT probably not needed
     ,group_concat(i.name_brown) AS interesticons -- DISTINCT probably not needed
   FROM `vacancy_interests` AS `vi`
   JOIN 
    ( SELECT interest_id
      FROM `interests`
      GROUP BY interest_id
      HAVING Max(interest_id) IN (3, 4, 6, 7, 9, 10)  
    ) AS `i`
   ON `vi`.`interest_id` = `i`.`interest_id`
 ) AS `vi`
ON `v`.`vacancy_id` = `vi`.`vacancy_id`
...

现在使用连接将 accessibility_id 添加到类似的 Derived Table。当您应用所有过滤条件时,将整个 Select 放入另一个 Derived Table 中,包括 LIMIT (减少行数),最后连接剩余的表(如果可能再次使用内部连接)