什么会导致连接比划分为两个查询慢?
Posted
技术标签:
【中文标题】什么会导致连接比划分为两个查询慢?【英文标题】:What can cause a join to be slower than dividing to two queries? 【发布时间】:2012-12-25 10:16:28 【问题描述】:我正在从多个表执行 JOIN 以执行分面搜索。当避免 JOIN 并将查询分成两个不同的查询时,我注意到性能有了很大的提升,所以我假设我的 JOIN 没有优化。
结构:
-- tags
userId | tagId
1 3
1 4
2 3
2 9
-- search
userId | number | countryId | stateId ...
1 13 221 55
-- countries
countryId | countryName
221 Somewhere
-- users
userId | profileImageLink
1 | <photo link>
我正在尝试提取所有具有标签的用户,根据 search.number 进行排序并从其他表中获取元数据。查询:
SELECT
search.*, users.a, users.b, users.c, users.d, users.e, users.f, countries.location_country, states.location_state, cities.location_city
FROM search
RIGHT JOIN tags
ON search.user_id = tags.user_id
LEFT JOIN users
ON users.user_id=search.user_id
LEFT JOIN countries
ON countries.countryId=search.countryId
LEFT JOIN states
ON states.countryId=search.countryId AND states.stateId=search.stateId
LEFT JOIN cities
ON cities.countryId=search.countryId AND cities.stateId=search.stateId AND cities.cityId=search.cityId
WHERE
tags.skillId =52772
ORDER BY
search.number DESC LIMIT 0,200
我注意到将 JOIN 删除到 users 表(然后再做)会使查询速度更快。如何优化它以在同一个查询中工作?我尝试将 FROM 更改为标签而不是搜索,但这不起作用...
这是 EXPLAIN 显示的内容:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE tags ref skill_user,skillId skill_user 4 const 184854 Using index; Using temporary; Using filesort
1 SIMPLE search eq_ref user_id user_id 4 tags.user_id 1
1 SIMPLE countries eq_ref PRIMARY PRIMARY 2 search.countryId 1
1 SIMPLE states eq_ref PRIMARY,state PRIMARY 3 search.stateId 1
1 SIMPLE cities eq_ref PRIMARY,city PRIMARY 3 search.cityId 1
1 SIMPLE users eq_ref user_id user_id 4 search.user_id 1
EXPLAIN without the LEFT JOIN users:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE tags ref skill_user,skillId skill_user 4 const 155870 Using index
1 SIMPLE search eq_ref user_id user_id 4 tags.user_id 1
1 SIMPLE countries eq_ref PRIMARY PRIMARY 2 search.countryId 1
1 SIMPLE states eq_ref PRIMARY,state PRIMARY 3 search.stateId 1
1 SIMPLE cities eq_ref PRIMARY,city PRIMARY 3 search.cityId 1
回答中建议的查询说明:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE tags index NULL userid_skill 8 NULL 22689539 Using where; Using index; Using temporary; Using filesort
1 SIMPLE search eq_ref user_id user_id 4 tags.user_id 1
1 SIMPLE users eq_ref user_id user_id 4 search.user_id 1
1 SIMPLE countries eq_ref PRIMARY PRIMARY 2 search.countryId 1
1 SIMPLE states eq_ref PRIMARY,state PRIMARY 3 search.stateId 1
1 SIMPLE cities eq_ref PRIMARY,city PRIMARY 3 search.cityId 1
【问题讨论】:
您的列(在连接列上)是否正确编入索引?无关紧要,我建议避免这么多连接。我认为当您的数据大小可能会增长时,拆分查询是一个更好的主意。 索引你的连接列,也许你的 where 子句中的 where 列...不确定我建议的内容是否被推荐 你能发布你的查询的“解释”结果吗?只需在查询前添加 EXPLAIN 关键字 @sdespont 将 EXPLAIN 输出添加到问题中 可能选择了错误的索引:skill_user 而不是 SkillId。这意味着它必须读取所有标签并加入所有其他标签。 SkillId = 52772 你有多少个标签?可以通过在RIGHT JOIN tags
之后添加FORCE INDEX(skillId)
来完成快速检查(我认为)。
【参考方案1】:
在表上创建索引以运行以下查询:
Table ColumnName
------ ----------
tags user_id, skillid (Both column in one index)
试试这个:
SELECT s.*, u.a, u.b, u.c, u.d, u.e, u.f, c.location_country,
st.location_state, ct.location_city
FROM tags t
LEFT JOIN search s ON t.user_id = s.user_id
LEFT JOIN users u ON t.user_id = u.user_id
LEFT JOIN countries c ON s.countryId = c.countryId
LEFT JOIN states st ON s.stateId = st.stateId
LEFT JOIN cities ci ON s.cityId= ct.cityId
WHERE t.skillId =52772
ORDER BY s.number DESC
LIMIT 0,200
编辑
使用适当的索引尝试这两个查询,让我知道以下查询是否适合您。
SELECT s.*, u.a, u.b, u.c, u.d, u.e, u.f, c.location_country,
st.location_state, ct.location_city
FROM (SELECT user_id FROM tags WHERE t.skillId = 52772) AS t
LEFT JOIN search s ON t.user_id = s.user_id
LEFT JOIN users u ON t.user_id = u.user_id
LEFT JOIN countries c ON s.countryId = c.countryId
LEFT JOIN states st ON s.stateId = st.stateId
LEFT JOIN cities ci ON s.cityId= ct.cityId
ORDER BY s.number DESC
LIMIT 0,200;
或
SELECT s.*, u.a, u.b, u.c, u.d, u.e, u.f, c.location_country,
st.location_state, ct.location_city
FROM (SELECT t.user_id, s.* FROM tags t
LEFT JOIN search s ON t.user_id = s.user_id
WHERE t.skillId = 52772
ORDER BY s.number DESC
LIMIT 0,200) AS t
LEFT JOIN users u ON t.user_id = u.user_id
LEFT JOIN countries c ON s.countryId = c.countryId
LEFT JOIN states st ON s.stateId = st.stateId
LEFT JOIN cities ci ON s.cityId= ct.cityId
ORDER BY s.number DESC
LIMIT 0,200;
【讨论】:
当你运行查询时,上传你的 EXPLAIN 语句的输出,这样我就可以理解它不能正常工作 这是我一开始的想法(更改 FROM),但它仍然是一样的。我再次运行它以确保,并且解释也显示了同样的事情。而且他不使用两列的索引,尽管存在一个 按user_id和skillid的顺序在两列上创建合并索引。并在查询中将该索引用作 LEFT JOIN search s USE INDEX (index_1) ON t.user_id = s.user_id。并更新我。 我拥有的索引是 SkillId -> user_id,我按照你的建议添加了 user_id -> SkillId 并尝试强制使用索引,但解释显示了更糟糕的情况(现在首先超过 22M一步使用 WHERE) 您是否创建了合并索引或两者都是单独的索引?以上是关于什么会导致连接比划分为两个查询慢?的主要内容,如果未能解决你的问题,请参考以下文章