简体   繁体   English

MySQL专家:为什么2个查询给出不同的“解释”索引使用结果?

[英]MySQL gurus: Why 2 queries give different 'explain' index use results?

This query: 该查询:

explain 
SELECT `Lineitem`.`id`, `Donation`.`id`, `Donation`.`order_line_id` 
  FROM `order_line` AS `Lineitem` 
       LEFT JOIN `donations` AS `Donation` 
       ON (`Donation`.`order_line_id` = `Lineitem`.`id`) 
 WHERE `Lineitem`.`session_id` = '1'

correctly uses the Donation.order_line_id and Lineitem.id indexes, shown in this EXPLAIN output: 正确使用此EXPLAIN输出中显示的Donation.order_line_idLineitem.id索引:

id  select_type     table       type    possible_keys   key             key_len     ref         rows    Extra
1   SIMPLE          Lineitem    ref     session_id      session_id      97          const       1       Using where; Using index
1   SIMPLE          Donation    ref     order_line_id   order_line_id   4           Lineitem.id 2       Using index

However, this query, which simply includes another field: 但是,此查询仅包含另一个字段:

explain 
SELECT `Lineitem`.`id`, `Donation`.`id`, `Donation`.`npo_id`, 
       `Donation`.`order_line_id` 
  FROM `order_line` AS `Lineitem`  
       LEFT JOIN `donations` AS `Donation` 
       ON (`Donation`.`order_line_id` = `Lineitem`.`id`) 
 WHERE `Lineitem`.`session_id` = '1'

Shows that the Donation table does not use an index: 显示Donation表未使用索引:

id  select_type     table       type    possible_keys   key             key_len     ref     rows    Extra
1   SIMPLE          Lineitem    ref     session_id      session_id      97          const   1       Using where; Using index
1   SIMPLE          Donation    ALL     order_line_id   NULL            NULL        NULL    3

All of the _id fields in the tables are indexed, but I can't figure out how adding this field into the list of selected fields causes the index to be dropped. 表中的所有_id字段都已建立索引,但我无法弄清楚如何将此字段添加到所选字段列表中会导致索引被删除。

As requested by James C, here are the table definitions: 根据James C的要求,以下是表定义:

CREATE TABLE `donations` (
`id` int(10) unsigned NOT NULL auto_increment,
`npo_id` int(10) unsigned NOT NULL,
`order_line_detail_id` int(10) unsigned NOT NULL default '0',
`order_line_id` int(10) unsigned NOT NULL default '0',
`created` datetime default NULL,
`modified` datetime default NULL,
PRIMARY KEY  (`id`),
KEY `npo_id` (`npo_id`),
KEY `order_line_id` (`order_line_id`),
KEY `order_line_detail_id` (`order_line_detail_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8

CREATE TABLE `order_line` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`order_id` bigint(20) NOT NULL,
`npo_id` bigint(20) NOT NULL default '0',
`session_id` varchar(32) collate utf8_unicode_ci default NULL,
`created` datetime default NULL,
PRIMARY KEY  (`id`),
KEY `order_id` (`order_id`),
KEY `npo_id` (`npo_id`),
KEY `session_id` (`session_id`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8

I also did some reading about cardinality, and it looks like both the Donations.npo_id and Donations.order_line_id have a cardinality of 2. Hopefully this suggests something useful? 我还阅读了一些有关基数的信息,看起来Donations.npo_idDonations.order_line_id的基数均为2。希望这表明有用吗?

I'm thinking that a USE INDEX might solve the problem, but I'm using an ORM that makes this a bit tricky, and I don't understand why it wouldn't grab the correct index when the JOIN specifically names indexed fields?!? 我以为USE INDEX可能会解决问题,但是我使用的ORM使得此操作有些棘手,而且我不明白为什么当JOIN专门命名索引字段时,为什么它无法获取正确的索引? !

Thanks for your brainpower! 感谢您的智慧!

The first explain has "uses index" at the end. 第一个解释的末尾有“使用索引”。 This means that it was able to find the rows and return the result for the query by just looking at the index and not having to fetch/analyse any row data. 这意味着它仅通过查看索引即可找到行返回查询结果,而不必获取/分析任何行数据。

In the second query you add a row that's likely not indexed. 在第二个查询中,添加可能未建立索引的行。 This means that MySQL has to look at the data of the table. 这意味着MySQL必须查看表的数据。 I'm not sure why the optimiser chose to do a table scan but I think it's likely that if the table is fairly small it's easier for it to just read everything than trying to pick out details for individual rows. 我不确定为什么优化器选择进行表扫描,但是我认为,如果表很小,则只读取所有内容比尝试为单个行选择详细信息要容易。

edit: I think adding the following indexes will improve things even more and let all of the join use indexes only: 编辑:我认为添加以下索引将进一步改善性能,并允许所有联接仅使用索引:

ALTER TABLE order_line ADD INDEX(session_id, id);
ALTER TABLE donations ADD INDEX(order_line_id, npo_id, id)

This will allow order_line to to find the rows using session_id and then return id and also allow donations to join onto order_line_id and then return the other two columns. 这将允许order_line使用session_id查找行,然后返回id ,还允许donations加入order_line_id ,然后返回其他两列。

Looking at the auto_increment values can I assume that there's not much data in there. 查看auto_increment值,我可以假设那里没有太多数据。 It's worth noting that the amount of data in the tables will have an effect on the query plan and it's good practice to put some sample data in there to test things out. 值得注意的是,表中的数据量将对查询计划产生影响,并且优良作法是在其中放置一些样本数据以进行测试。 For more detail have a look in this blog post I made some time back: http://webmonkeyuk.wordpress.com/2010/09/27/what-makes-a-good-mysql-index-part-2-cardinality/ 有关更多详细信息,请参阅我在此博客上发表的文章: http : //webmonkeyuk.wordpress.com/2010/09/27/what-makes-a-good-mysql-index-part-2-cardinality/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM