Mysql Query中的索引表现得无差别

时间:2013-01-28 17:56:44

标签: mysql sql indexing query-optimization

我有一个问题:

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');

(请注意,它缺少用于我的sphinx索引的实际大型查询,代表问题) 此查询大约需要 3.5秒(从id = 1..5000修改范围使其大约 15秒)。

users表有大约35000个条目,而comments表有大约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 |主要|用户|范围|主   |主要| 4 | NULL | 1992年|用在哪里;使用索引|

     

| 2 |相关的子信息| d360_core_comments |所有| NULL | NULL | NULL | NULL | 6901 |用在哪里;使用临时|

这里的单个子查询(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秒:

  

的MySQL&GT;从d360_core_comments中解释select owner_id   CONTENT_TYPE = 'Community20 ::主题';

     

| id | select_type |表|类型| possible_keys |关键| key_len | ref |行|额外的|   

<小时/>   | 1 |简单| d360_core_comments |所有| 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');) 现在在~0秒内运行,并按照以下说明进行操作:

  

的MySQL&GT;解释从id> 1和id的用户中选择SQL_NO_CACHE id   &lt; 1000和id in(从d360_core_comments中选择owner_id,其中   CONTENT_TYPE = 'Community20 ::主题');

     

| id | select_type |表|类型| possible_keys |关键|   key_len | ref |行|额外的|   

<小时/>   | 1 |主   |用户|范围|主要|主要| 4 | NULL | 1992年|用在哪里;使用索引|

     

| 2 |相关的子信息|    d360_core_comments | index_subquery | tmp_user | tmp_user | 5 | func | 34 |使用where |

所以我的主要问题是:

  • 如果在我的子查询中使用的表上定义的索引没有在我的实际子查询中使用,那么它在这里如何优化查询?
  • 首先,为什么第一个查询在实际的子查询和主查询独立时花费了这么多时间?

4 个答案:

答案 0 :(得分:3)

在没有索引的完整查询中似乎发生的事情是MySQL将构建(某种)子查询生成的所有owner_id的临时表。然后,对于与user约束匹配的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。

答案 1 :(得分:2)

如果您阅读了MySQL参考手册的这一部分:Optimizing Subqueries with EXISTS Strategy,您将看到查询优化器从以下位置转换子查询条件:

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)上的索引无用的原因,但在考虑转换的子查询时它很有用。

答案 2 :(得分:1)

你应该知道的第一件事是MySQL无法优化依赖子查询,这是一个长期以来众所周知的MySQL缺陷,这将在MySQL 6.x中修复(只是google for“mysql dependent subquery) “你会看到的。”也就是说,子查询基本上是为users表中的每个匹配行执行的。由于您有其他条件,因此总体执行时间取决于该条件。解决方案是用子连接替换子查询(这是您期望从MySQL中获得的优化)。

其次,子查询中存在语法错误,我认为owner_id存在一个条件。因此,当您在owner_id上添加索引时,它会被使用,但对于第二个条件(因此没有using index)是不够的,但是EXPLAIN中根本没有提到的原因是问题(我认为是因为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;

P.S。 (content_type, owner_id)上的复合索引甚至可以更好地用于查询。

答案 3 :(得分:0)

第1步:使用id BETWEEN x AND y代替id >= x AND id <= y。您可能会发现一些令人惊讶的收益,因为它的索引更好。

第2步:调整您的子SELECT进行过滤,这样就不必两次了:

SELECT SQL_NO_CACHE id 
  FROM users
 WHERE id IN (SELECT owner_id 
                FROM comments
               WHERE content_type='Some_string' 
                 AND owner_id BETWEEN 1 AND 1000);

您的陈述中似乎有多处错误。例如,您选择2到999,可能两端都是一个,并且子选择无效。