我有一个页面,用户可以使用非常广泛的过滤器集搜索职位空缺。这些过滤器中有两个是复选框,用户可以在其中选择兴趣和可访问性。如果选中该选项,则必须在一定的空缺中至少存在其中之一才能显示出来。
我正在使用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。我不确定这是否会导致大量延迟(实际上没有那么多行)。
有人看到我的查询出了什么问题并使我的页面崩溃吗?
您当前的Select有几个问题:
尝试尽快应用过滤器和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的派生表中(减少行数),最后联接其余表(如果可能的话,再次使用内部联接)