我遇到了MySQL的奇怪行为。 查询执行(即解释[QUERY]所示的索引的使用)和执行所需的时间取决于where子句的元素。
以下是发生问题的查询:
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秒。
我一直认为查询的选择部分不会影响引擎对索引的使用,也不会以这种方式影响性能。
退出属性不是解决方案,还有更多属性我必须选择。 更糟糕的是,使用形式的查询甚至更复杂,这使得性能问题成为一个大问题。
所以我的问题是: 1)这种奇怪行为的原因是什么? 2)如何解决性能问题?
感谢您的帮助! GRED
答案 0 :(得分: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))
答案 1 :(得分:0)
如果可能,MySQL将从索引返回数据,从而保存整行不被加载。这样,所选列可以影响索引选择。
考虑到这一点,将所有必需的列添加到索引可以更加高效,尤其是在仅选择一小部分列的情况下。