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

问题描述 投票:1回答:1

我有一个页面,用户可以使用非常广泛的过滤器集搜索职位空缺。这些过滤器中有两个是复选框,用户可以在其中选择兴趣和可访问性。如果选中该选项,则必须在一定的空缺中至少存在其中之一才能显示出来。

我正在使用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表使我的搜索页在使用时完全崩溃。在我有大约100个空缺的localhost上,它工作良好且快速。但是我的生产服务器(大约有11k个空缺)全部瘫痪了,搜索大约需要5-6分钟才能完成。

我如上所述在MySQL工作台中执行了查询,查询计算大约需要52秒。

如果我执行相同的查询,但是只选择了感兴趣的内容(这意味着我不通过LEFT JOIN加载可访问性表,那么它的工作速度非常快(大约0.1秒)。反之,这实际上是行不通的,我尝试删除感兴趣的左连接,查询速度更快,但仍约为16秒。请注意,我确实需要将兴趣加入其中,因此这并不是一个选项,仅用于调试。

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

有人看到我的查询出了什么问题并使我的页面崩溃吗?

mysql sql query-performance
1个回答
3
投票

您当前的Select有几个问题:

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

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

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。当您应用所有过滤条件时,将整个Select放入另一个包含LIMIT的派生表中(减少行数),最后联接其余表(如果可能的话,再次使用内部联接)

© www.soinside.com 2019 - 2024. All rights reserved.