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)上的组合索引对查询甚至会更好。

    链接地址: http://www.djcxy.com/p/25081.html

    上一篇: Indexes in Mysql Query behaving indifferently

    下一篇: MYSQL optimize 'IN' AND 'ORDER BY'