per*_*lis 6 mysql indexing myisam innodb
我有这些小桌子,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
Run Code Online (Sandbox Code Playgroud)
我插入了100个类别和1000个项目.
如果我运行这个:
EXPLAIN SELECT item.id,category.name AS category_name FROM item JOIN category ON item.category_id=category.id;
Run Code Online (Sandbox Code Playgroud)
然后,如果表的引擎是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 |
+----+-------------+----------+-------+---------------+-------------+---------+--------------------+------+-------------+
Run Code Online (Sandbox Code Playgroud)
然而,如果我切换到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 | |
+----+-------------+----------+--------+---------------+---------+---------+-------------------------+------+-------+
Run Code Online (Sandbox Code Playgroud)
我的问题是,为什么处理索引的方式存在差异?
在InnoDB中,任何二级索引内部都包含表的主键列。因此列 (name) 上的索引名称隐式地存在于列 (name, id) 上。
这意味着 EXPLAIN 将您对类别表的访问显示为“索引扫描”(这在类型列中显示为“索引”)。通过扫描索引,它还可以访问 id 列,它使用该列来查找第二个表 item 中的行。
然后,它还利用 (category_id) 上的项目索引(实际上是 (category_id, id)),并且只需读取索引即可为您的选择列表获取 item.id。根本不需要阅读表格(这在额外列中显示为“使用索引”)。
MyISAM 不会以这种方式存储主键和辅助键,因此它无法获得相同的优化。对类别表的访问是“ALL”类型,这意味着表扫描。
我希望对 MyISAM 表项的访问是“ref”,因为它使用 (category_id) 上的索引查找行。ANALYZE TABLE item
但是,如果表中的行很少,或者自创建索引以来尚未执行过操作,优化器可能会得到有偏差的结果。
回复您的更新:
看起来优化器更喜欢索引扫描而不是表扫描,因此它抓住机会在 InnoDB 中进行索引扫描,并将类别表放在第一位。优化器决定对表重新排序,而不是按照您在查询中指定的顺序使用表。
在 MyISAM 表中,无论它选择先访问哪个表,都会进行一次表扫描,但通过将类别表放在第二位,它会连接到类别的主键索引而不是项目的辅助索引。优化器更喜欢查找而不是唯一键或主键(类型“eq_ref”)。