为什么Mysql优化器在执行没有order by子句的“select * from lookup”时选择辅助索引。
这只是一个侥幸,还是这是一个幕后优化,假设因为您添加了一个辅助索引,它比主键更重要。
我希望结果按主键排序,因为对所有叶节点的扫描可以提供回答此查询所需的所有数据。
为了重现,我创建了一个简单的键/值对表(注意不是auto_increment)
create table lookup (
id int not null,
primary key (id),
name varchar(25),
unique k_name (name)
) engine=innodb;
以随机非字母顺序插入一些数据
insert into lookup values(1, "Zebra"),(2, "Aardvark"),(3, "Fish"),(4,"Dog"),(5,"Cat"),(6,"Mouse");
查询数据(这是我希望数据按主键顺序返回的地方)
mysql> select * from lookup;
+----+----------+
| id | name |
+----+----------+
| 2 | Aardvark |
| 5 | Cat |
| 4 | Dog |
| 3 | Fish |
| 6 | Mouse |
| 1 | Zebra |
+----+----------+
6 rows in set (0.00 sec)
但实际上并非如此-似乎已经完成了对k_name叶节点的扫描。
mysql> explain select * from lookup;
+----+-------------+--------+-------+---------------+--------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+--------+---------+------+------+-------------+
| 1 | SIMPLE | lookup | index | NULL | k_name | 28 | NULL | 6 | Using index |
+----+-------------+--------+-------+---------------+--------+---------+------+------+-------------+
1 row in set (0.00 sec)
对我来说,这意味着Mysql使用k_name作为覆盖索引来返回数据。如果我删除k_name索引,则数据将按主键顺序返回。如果我添加另一个未索引的列,则数据将按主键顺序返回。
关于我的设置的一些基本信息。
mysql> show table status like 'lookup'\G
*************************** 1. row ***************************
Name: lookup
Engine: InnoDB
Version: 10
Row_format: Compact
Rows: 6
Avg_row_length: 2730
Data_length: 16384
Max_data_length: 0
Index_length: 16384
Data_free: 0
Auto_increment: NULL
Create_time: 2011-11-15 10:42:35
Update_time: NULL
Check_time: NULL
Collation: latin1_swedish_ci
Checksum: NULL
Create_options:
Comment:
1 row in set (0.00 sec)
mysql> select version();
+------------+
| version() |
+------------+
| 5.5.15-log |
+------------+
1 row in set (0.00 sec)
4条答案
按热度按时间lztngnrs1#
实际上,聚集索引(akagen_clustre_index)的填充顺序除了rowid顺序之外没有任何规律或原因。实际上不可能按id顺序对rowid进行排序。
在InnoDB中,非聚集索引(也称为辅助索引)中的记录包含辅助索引中没有的行的主键列。InnoDB使用此主键值在聚集索引中搜索行。
次索引控制顺序。但是,每个次索引条目都有一个指向正确行的主键条目。另外,考虑一下您提到的k_name的覆盖索引场景。
现在,让我们换个主题,讨论一下PRIMARY KEY和k_name:
问题:主键和k_name中,谁的原始查询请求的列更多?
答复:k_name,因为它包含name和id(id是内部的,因为它是PRIMARY KEY)覆盖索引k_name比主键更能满足查询。
现在,如果查询是
SELECT * FROM ORDER BY id
,您的EXPLAIN PLAN应该如下所示:MySQL查询优化器不会指定顺序,而是选择最适合查询的索引。
*k_name不是辅助索引,因为它是候选键,就像主键一样。
您根本无法操纵行的顺序。下面是这一点的证明:
qmelpv7a2#
这是因为InnoDB二级索引也包含主键列,因此MySQL能够直接从二级索引中获取所有相关数据,而无需接触数据行,因此节省了磁盘IO。
参考文献:
2izufjch3#
两种索引在获取查询数据方面都是同样有效的,所以我猜优化程序只是放弃了“这就行了”
添加另一个唯一索引,可能是因为它们都同样有效,一些“FindBestIndex”例程在读取最后一个索引时退出。
这也不是我所期望的行为,尽管如果我关心顺序,我会添加一个按id排序的命令,让优化器选择主键,而不是进行两次传递和排序。
px9o7tmv4#
我想你没有理解类型列。类型列'index'意味着一个完整的索引扫描。当这种情况下,如果'extra'列有'using index',这意味着mysql可以从索引中获得查询所需的所有数据,而不需要求助于实际的表行。所以这里的引擎,而不是去看那些行(这通常代价很高)会使用包含查询所需的所有数据的索引。(id,在你的例子中)作为数据,也就是说,如果你在二级索引中查找一个键,你就得到了表记录的主键,因为你只是要求所有的值,这就足够迭代二级索引来得到你需要的了。
如果引擎选择遍历主键,主键会直接指向实际的表行,Mysql会尽量避免这种行为,因为这样做通常效率很低,因为行包含的数据通常比索引包含的数据多,可能需要执行更多的IO。
http://dev.mysql.com/doc/refman/5.0/en/explain-output.html