2010-09-22 88 views
0

我遇到了MySQL的奇怪行为。 查询执行(即explain [QUERY]所示的索引使用)和执行所需的时间取决于where子句的元素。为什么SELECT语句会影响MySQL中的查询执行和性能?

这里是哪里出现问题的查询:

select distinct 
e1.idx, el1.idx, r1.fk_cat, r2.fk_cat 
from ent e1, ent_leng el1, rel_c r1, _tax_c t1, rel_c r2, _tax_c t2 
where el1.fk_ent=e1.idx 
and r1.fk_ent=e1.idx and ((r1.fk_cat=43) or (r1.fk_cat=t1.fk_cat1 and t1.fk_cat2=43)) 
and r2.fk_ent=e1.idx and ((r2.fk_cat=10) or (r2.fk_cat=t2.fk_cat1 and t2.fk_cat2=10)) 

相应的解释输出:

| id | select_type | table | type | possible_keys   | key  | key_len | ref   | rows | Extra      
+----+-------------+-------+--------+-------------------------+---------+---------+---------------+-------+------------------------------------ 
| 1 | SIMPLE  | el1 | index | fk_ent     | fk_ent | 4  | NULL   | 15002 | Using index; Using temporary 
| 1 | SIMPLE  | e1 | eq_ref | PRIMARY     | PRIMARY | 4  | DB.el1.fk_ent |  1 | Using index 
| 1 | SIMPLE  | r1 | ref | fk_ent,fk_cat,fks  | fks  | 4  | DB.e1.idx  |  1 | Using where; Using index 
| 1 | SIMPLE  | r2 | ref | fk_ent,fk_cat,fks  | fks  | 4  | DB.el1.fk_ent |  1 | Using index 
| 1 | SIMPLE  | t1 | index | fk_cat1,fk_cat2,fk_cats | fk_cats | 8  | NULL   | 69 | Using where; Using index; Distinct; 
| |    |  |  |       |   |   |    |  | Using join buffer 
| 1 | SIMPLE  | t2 | index | fk_cat1,fk_cat2,fk_cats | fk_cats | 8  | NULL   | 69 | Using where; Using index; Distinct; 
                              | Using join buffer 

正如你可以看到一列的索引具有相同的名称列它属于。我还添加了一些无用的索引以及使用的索引,只是为了看看它们是否改变了执行(它们不执行)。

执行需要4.5秒。

当我列entl1.name添加到选择部分(没有别的改变),在EL1指数fk_ent不能使用任何更多:

| id | select_type | table | type | possible_keys   | key  | key_len | ref   | rows | Extra      
+----+-------------+-------+--------+-------------------------+---------+---------+---------------+-------+------------------------------------ 
| 1 | SIMPLE  | el1 | ALL | fk_ent     | NULL | NULL | NULL   | 15002 | Using temporary 

执行现在只需〜8.5秒。

我一直认为查询的select部分不会影响引擎的索引使用,也不会以这种方式影响性能。

退出该属性不是一个解决方案,并且还有更多的属性需要我选择。 更糟糕的是,用过的表单中的查询甚至更复杂一些,这使得性能问题成为一个大问题。

所以我的问题是: 1)这种奇怪的行为是什么原因? 2)如何解决性能问题?

感谢您的帮助! Gred

回答

2

这是DISTINCT限制。您可以将其视为另一个WHERE限制。当您更改选择列表时,您确实正在为DISTINCT限制更改WHERE子句,现在优化器决定必须执行表扫描,因此它可能不会使用您的索引。

编辑:

不知道这是否有帮助,但如果我理解正确的数据,我想你可以摆脱DISTINCT限制这样的:

select 
e1.idx, el1.idx, r1.fk_cat, r2.fk_cat 
from ent e1 
    Inner Join ent_leng el1 ON el1.fk_ent=e1.idx 
    Inner Join rel_c r1 ON r1.fk_ent=e1.idx 
    Inner Join rel_c r2 ON r2.fk_ent=e1.idx 
where 
((r1.fk_cat=43) or Exists(Select 1 From _tax_c t1 Where r1.fk_cat=t1.fk_cat1 and t1.fk_cat2=43)) 
and 
((r2.fk_cat=10) or Exists(Select 1 From _tax_c t2 Where r2.fk_cat=t2.fk_cat1 and t2.fk_cat2=10)) 
+0

听起来很合理。但是这不是问题的解决方案(我不得不处理一个巨大的,准冗余的结果集或使用复杂的索引)。而奇怪的是,删除DISTINCT并不会改变EXPLAIN输出,也不会加快查询速度,因为我刚刚检查过。 – GredPapp 2010-09-22 15:34:58

+0

刚刚测试过:您的查询似乎与我的结果相同 - 而且速度更快!谢谢!我想我必须更深入地了解SQL以适当地使用它。 – GredPapp 2010-09-22 18:33:58

0

MySQL将返回如果可能的话,从索引中获取数据,保存整个行加载。这样,选定的列可以影响索引选择。

考虑到这一点,将所有必需的列添加到索引可以更加高效,特别是在仅选择一小部分列的情况下。