我有这些小表,item
和category
:
CREATE TABLE `item` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(150) NOT NULL,
`category_id` mediumint(8) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `name` (`name`),
KEY `category_id` (`category_id`)
) CHARSET=utf8
CREATE TABLE `category` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(150) NOT NULL,
PRIMARY KEY (`id`),
KEY `name` (`name`)
) CHARSET=utf8
我已插入100个类别和1000个项目。
如果我这样做:
EXPLAIN SELECT item.id,category.name AS category_name FROM item JOIN category ON item.category_id=category.id;
然后,如果表的引擎是InnoDB,我得到:
+----+-------------+----------+-------+---------------+-------------+---------+--------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+-------------+---------+--------------------+------+-------------+
| 1 | SIMPLE | category | index | PRIMARY | name | 452 | NULL | 103 | Using index |
| 1 | SIMPLE | item | ref | category_id | category_id | 3 | dbname.category.id | 5 | Using index |
+----+-------------+----------+-------+---------------+-------------+---------+--------------------+------+-------------+
然而,如果我切换到MyISAM(alter table engine=myisam
),我得到:
+----+-------------+----------+--------+---------------+---------+---------+-------------------------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+--------+---------------+---------+---------+-------------------------+------+-------+
| 1 | SIMPLE | item | ALL | category_id | NULL | NULL | NULL | 1003 | |
| 1 | SIMPLE | category | eq_ref | PRIMARY | PRIMARY | 3 | dbname.item.category_id | 1 | |
+----+-------------+----------+--------+---------------+---------+---------+-------------------------+------+-------+
我的问题是,为什么处理索引的方式存在差异?
答案 0 :(得分:4)
在InnoDB中,任何二级索引都在内部包含表的主键列。因此列(名称)上的索引 name 隐含在列(name,id)上。
这意味着EXPLAIN将您对类别表的访问权限显示为“索引扫描”(这在类型列中显示为“索引”)。通过扫描索引,它还可以访问id列,用于在第二个表item中查找行。
然后它还利用了(category_id)上的项索引,它实际上是(category_id,id),只需通过读取索引就可以为select-list获取item.id。根本不需要阅读该表(这在 Extra 列中显示为“使用索引”)。
MyISAM不以这种方式存储具有辅助密钥的主键,因此无法获得相同的优化。对类别表的访问是“ALL”类型,表示表扫描。
我希望MyISAM表项的访问权限为“ref”,因为它使用(category_id)上的索引查找行。但是,如果表中的行数非常少,或者自创建索引后没有完成ANALYZE TABLE item
,优化程序可能会得到偏差的结果。
重新更新:
看起来优化器更喜欢对表扫描进行索引扫描,因此它有机会在InnoDB中进行索引扫描,并将类别表放在第一位。优化程序决定重新排序表,而不是按照查询中给出的顺序使用表。
在MyISAM表中,将有一个表扫描它首先选择访问哪个表,但是通过将类别表放在第二个,它将连接到类别的PRIMARY键索引而不是item的辅助索引。优化器更喜欢查找唯一键或主键(键入“eq_ref”)。