MySQL 专家:为啥 2 个查询给出不同的“解释”索引使用结果?
Posted
技术标签:
【中文标题】MySQL 专家:为啥 2 个查询给出不同的“解释”索引使用结果?【英文标题】:MySQL gurus: Why 2 queries give different 'explain' index use results?MySQL 专家:为什么 2 个查询给出不同的“解释”索引使用结果? 【发布时间】:2011-04-23 22:52:07 【问题描述】:这个查询:
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'
正确使用 Donation.order_line_id
和 Lineitem.id
索引,显示在此 EXPLAIN 输出中:
但是,这个查询,它只包含另一个字段:
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'
表明Donation
表没有使用索引:
表中的所有_id
字段都已编入索引,但我无法弄清楚将此字段添加到选定字段列表中如何导致索引被删除。
根据 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
我还阅读了一些关于基数的文章,看起来 Donations.npo_id
和 Donations.order_line_id
的基数都是 2。希望这表明一些有用的信息?
我在想 USE INDEX
可能会解决问题,但我使用的 ORM 让这有点棘手,我不明白为什么它不会在 JOIN 时获取正确的索引名称索引字段?!?
感谢您的智慧!
【问题讨论】:
【参考方案1】:第一个解释的末尾有“使用索引”。这意味着它能够找到行并只通过查看索引而不需要获取/分析任何行数据来返回查询结果。
在第二个查询中,您添加了可能未编入索引的行。这意味着 mysql 必须查看表的数据。我不确定优化器为什么选择进行表扫描,但我认为如果表相当小,它可能更容易读取所有内容,而不是尝试挑选单个行的详细信息。
编辑:我认为添加以下索引会进一步改进,让所有连接只使用索引:
ALTER TABLE order_line ADD INDEX(session_id, id);
ALTER TABLE donations ADD INDEX(order_line_id, npo_id, id)
这将允许order_line
使用session_id
查找行,然后返回id
,还允许donations
加入order_line_id
,然后返回其他两列。
查看auto_increment
值,我可以假设其中没有太多数据。值得注意的是,表中的数据量会对查询计划产生影响,最好将一些示例数据放在那里进行测试。有关更多详细信息,请查看我之前发布的这篇博文:http://webmonkeyuk.wordpress.com/2010/09/27/what-makes-a-good-mysql-index-part-2-cardinality/
【讨论】:
实际上这可能不是 100% 正确的。您能否为表格添加SHOW CREATE TABLE
和完整的EXPLAIN
输出?
只是添加第二个索引有效,但我不明白为什么。所有这三个 id 都已编入索引 - 为什么在忽略 order_line_id 索引的情况下创建由这些 id 组成的索引?
我认为发生这种情况的原因是优化器认为使用单一索引没有任何优势。随着桌子大小的增加,我会密切关注这一点。谢谢!
对于每个打开的表 MySQL 只能使用其中一个索引,因此您需要通过创建复合索引来使用“尾随索引”,使其能够找到索引左侧的行,然后获取右侧更多列的数据。这是由于 B-TREE 索引的存储方式造成的。以上是关于MySQL 专家:为啥 2 个查询给出不同的“解释”索引使用结果?的主要内容,如果未能解决你的问题,请参考以下文章