我有一个简单的查询,它不能按预期工作。尽管有索引,但查询的连接部分会忽略它并执行全表扫描。这是查询
SELECT m0.id_field,
attr_73217_
FROM object_73195_ o
INNER JOIN master_slave m0
ON ( m0.id_object = 73130
OR m0.id_object = 82344)
AND ( m0.id_master = 73195
OR m0.id_master = 82413)
AND m0.id_slave_field = o.id
ORDER BY
o.id_order
EXPLAIN
命令返回以下行:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE m0 ALL id_object,id_master,id_slave_field,id_slave_field_2,id_object_2,id_object_3 \N \N \N 2782 Using where; Using temporary; Using filesort
1 SIMPLE o eq_ref PRIMARY PRIMARY 8 project.m0.id_slave_field 1 Using where
正如您所看到的,它不使用密钥,即使它是这样创建的:
ALTER TABLE master_slave ADD INDEX (id_object,id_master,id_slave_field);
有趣的是,如果我从m0.id_field
部分注释掉SELECT
,那么首先选择类型(由explain命令给出)变为range
,查询开始使用键id_object_3
以及非常重要的 - 它现在扫描master_slave
表中较少的行数。但问题是,我m0.id_field
部分需要select
。我想我需要对我的指数做些什么,但我不知道到底是什么。
修改 我试图添加另一个这样的键:
ALTER TABLE master_slave ADD INDEX (id_field);
ALTER TABLE master_slave ADD INDEX (id_object);
但是EXPLAIN
命令返回的是同一组行 - 没有键和全表扫描。整个问题是由select
部分中的m0.id_field引起的。
修改
我刚刚在master_slave表中添加了一堆索引:
ALTER TABLE master_slave ADD INDEX (id_field,id_object,id_master,id_slave_field);
ALTER TABLE master_slave ADD INDEX (id_object,id_field,id_master,id_slave_field);
ALTER TABLE master_slave ADD INDEX (id_object,id_master,id_field,id_slave_field);
ALTER TABLE master_slave ADD INDEX (id_object,id_master,id_slave_field,id_field);
每个索引都会降低扫描行数。我特别感谢 kordirko 。
答案 0 :(得分:1)
@Jacobian - 这不是你问题的答案
或者只是部分回答
我在这里写作是因为我的解释太长而且不适合评论。
如果select语句不包含m0.id_field
,则查询仅引用m0表中的3个字段:id_object,id_master,id_slave_field。
由于该表的3列中存在覆盖索引,因此显而易见的选择是扫描此索引而不是表。索引(磁盘上的索引文件)比表小得多,读取索引的成本低于读取表。
当索引包含查询检索到的所有必需列时,我们说覆盖索引,并且查询可以直接从索引检索所有信息 - >见:http://en.wikipedia.org/wiki/Database_index#Covering_index
如果将m0.id_field
添加到select子句中,则没有包含所有这4列的索引,在这种情况下,查询必须从表中读取此列的值。
它可以通过两种方式实现:
1.使用索引过滤行,然后使用从索引获得的主键(逐行 - 随机访问)访问表中的行。
2.扫描整个表格,不触及任何索引
在预期行数较小(<5%可能<10%的表)的情况下,第一种方法是好的。请记住,DBMS无法从磁盘读取一行,它始终必须读取整页!要获得一个大小的行,例如50个字节,它必须读取整个页面,其大小为5k或10k或更多(页面的长度取决于设置)。可以进行一些优化,例如MySql,在扫描索引时,首先在内存中收集PK值,然后对它们进行排序,最后使用这些PK按升序扫描表,以最小化从磁盘检索的页数。但它仍然是随机访问,它比顺序读取慢(磁盘必须寻找随机轨道的头部,而不是按轨道读取轨道)
如果预期的行数很大(在我们的例子中是表的34%),使用第二种方法(扫描整个表)要比首次扫描和过滤索引便宜得多,然后对扫描结果进行排序,然后使用从索引中检索的PK访问表。必须从磁盘读取的最终磁盘页数较少(扫描索引也必须从磁盘读取一些页面)。