Mysql Query中的索引表现得无所谓
我有一个查询:
select SQL_NO_CACHE id from users
where id>1 and id <1000
and id in ( select owner_id from comments and content_type='Some_string');
(请注意,它缺少用于我的狮身人面像索引的实际大型查询,代表问题)此查询花费大约3.5秒 (修改范围从id = 1..5000约15秒 )。
用户表约有35000个条目,评论表约有8000个条目。
上面的查询解释:
explain select SQL_NO_CACHE id from users
where id>1 and id <1000
and id in ( select distinct owner_id from d360_core_comments);
| id | select_type | 表| | 键入| possible_keys | 键| key_len | ref | 行| 额外|
| 1 | PRIMARY | 用户| 范围| PRIMARY | PRIMARY | 4 | NULL | 1992 | 在哪里使用; 使用index |
| 2 | 依赖子查询| d360_core_comments | ALL | NULL | NULL | NULL | NULL | 6901 | 在哪里使用; 使用临时|
其中单个子查询( select owner_id from d360_core_comments where content_type='Community20::Topic';
)中select owner_id from d360_core_comments where content_type='Community20::Topic';
将花费将近0.0秒。
但是,如果我在owner_id,content_type上添加索引 (请注意这里的顺序)
create index tmp_user on d360_core_comments (owner_id,content_type);
我的子查询在〜0.0秒内运行,并且使用了NO索引:
mysql>解释从d360_core_comments中选择owner_id,其中content_type ='Community20 :: Topic';
| id | select_type | 表| | 键入| possible_keys | 键| key_len | ref | 行| 额外|
| 1 | SIMPLE | d360_core_comments | ALL | NULL | NULL | NULL | NULL | 6901 | 使用where |
然而,现在我的主要查询( select SQL_NO_CACHE id from users where id>1 and id <1000 and id in ( select owner_id from d360_core_comments where content_type='Community20::Topic');
)现在在select SQL_NO_CACHE id from users where id>1 and id <1000 and id in ( select owner_id from d360_core_comments where content_type='Community20::Topic');
秒内运行,
mysql>解释从id> 1和id <1000和id in的用户中选择SQL_NO_CACHE id(从d360_core_comments where content_type ='Community20 :: Topic'中选择owner_id);
| id | select_type | 表| | 键入| possible_keys | 键| key_len | ref | 行| 额外|
| 1 | PRIMARY | 用户| 范围| PRIMARY | PRIMARY | 4 | NULL | 1992 | 在哪里使用; 使用index |
| 2 | 依赖子查询| d360_core_comments | index_subquery | tmp_user | tmp_user | 5 | func | 34 | 使用where |
所以我的主要问题是:
如果您阅读MySQL参考手册的这一部分:使用EXISTS
策略优化子查询,您将看到查询优化器将您的子查询条件转换为:
id in ( select distinct owner_id
from d360_core_comments
where content_type='Community20::Topic')
成:
exists ( select 1
from d360_core_comments
where content_type='Community20::Topic'
and owner_id = users.id )
这就是为什么当子查询作为独立查询进行测试时, (owner_id, content_type)
上的索引(owner_id, content_type)
,但在考虑转换的子查询时非常有用。
在没有索引的情况下,完全查询似乎会发生什么,MySQL将构建(某种)子查询生成的所有owner_id的临时表。 然后,对于与id约束匹配的users表中的每一行,将执行此临时构造中的查找。 目前还不清楚是否开销创建了临时构造,或者如果查找是次优执行的(以便所有元素都对来自外部查询的每一行进行线性匹配。
当您在owner_id上创建索引时,如果仅运行子查询,则这不会更改任何内容,因为它在owner_id上没有条件,索引也不覆盖content_type列。
但是,当您使用索引运行完整查询时,会提供更多信息,因为我们现在具有来自外部查询的值,该值应与索引所涵盖的owner_id匹配。 因此,执行现在似乎是运行外部查询的第一部分,并且对于每个匹配的行,通过owner_id执行索引查找。 换句话说,一个可能的执行计划是:
From Index-Users-Id Get all id matching id>1 and id <1000
For Each Row
Include Row If Index-Comment-OwnerId Contains row.Id
And Row Matches content_type='Some_string'
所以在这种情况下,运行1000(我假设)索引查找的工作比构建8000可能的owner_id的临时构造要快。 但这只是一个假设,因为我不太了解MySQL。
首先你应该知道的是,MySQL不能优化从属子查询,这是一个很长时间以来众所周知的MySQL不足之处,它将在MySQL 6.x中得到修复(只是谷歌为“依赖于mysql的子查询”,而你等着瞧)。 这就是子查询基本上是为users
表中的每个匹配行执行的。 由于您有附加条件,总体执行时间取决于该条件。 解决方案是用一个联接替换子查询(这是您期望从MySQL中获得的最佳优化)。
其次,你的子查询中有一个语法错误,我认为owner_id有一个条件。 因此,当您在owner_id
上添加索引时,会使用它,但对于第二个条件(因此没有using index
)是不够的,但为什么在EXPLAIN
中根本没有提到这个问题(我认为是因为users.id
的条件users.id
)
第三,我不知道为什么你需要id > 1 and id < 5000
条件,但你应该明白,这是两个范围条件,需要非常准确,有时非显而易见和数据相关的索引方法(与平等比较条件),如果你实际上不需要它们,只用来解释为什么查询需要这么长时间,那么这是一个糟糕的主意,他们不会发光。
如果条件是必需的,并且owner_id
上的索引仍然存在,我将按如下方式重写查询:
SELECT id
FROM (
SELECT owner_id as id
FROM comments
WHERE owner_id < 5000 AND content_type = 'some_string'
) as ids
JOIN users ON (id)
WHERE id > 1;
PS (content_type, owner_id)
上的组合索引对查询甚至会更好。