我通过“高性能MySQL”一书研究MySQL索引细节,我无法理解一件事。
如书所述,(第124页使用索引扫描进行排序)
MySQL有两种生成有序结果的方法:它可以使用一个文件排序, 或者它可以按顺序扫描索引。
通过索引对结果进行排序仅在索引的顺序为时才有效 与ORDER BY子句完全相同,所有列都按顺序排序 相同的方向(上升或下降)。
ORDER BY子句也与查询查询具有相同的限制:它 需要形成索引的最左前缀。在所有其他情况下, MySQL使用filesort。
此外,作者使用MySQL Sakila的示例DataBase给出了一些示例 [http://dev.mysql.com/doc/sakila/en/][1]
第一个例子很好用:
标准Sakila示例数据库中的租赁表具有索引 (rental_date,inventory_id,customer_id):
CREATE TABLE rental (
...
PRIMARY KEY (rental_id),
UNIQUE KEY rental_date (rental_date,inventory_id,customer_id),
KEY idx_fk_inventory_id (inventory_id),
KEY idx_fk_customer_id (customer_id),
KEY idx_fk_staff_id (staff_id),
...
);
MySQL使用rental_date索引来为您排序以下查询 从EXPLAIN中缺少一个文件排序可以看出:
> mysql> EXPLAIN SELECT
> rental_id, staff_id FROM sakila.rental
> -> WHERE rental_date = '2005-05-25'
> -> ORDER BY inventory_id, customer_id\G
> *************************** 1. row ***************************
> type: ref
> possible_keys: rental_date
> key: rental_date
> rows: 1
> Extra: Using where
这很有效,即使ORDER BY子句本身不是一个 最左边的索引前缀,因为我们指定了相等 索引中第一列的条件。
重要的是要注意:它们在where子句中使用索引列,但在SELECT查询中使用不同的列。
第二个例子以简短的方式显示:
以下查询也有效,因为ORDER BY中有两列 是索引的最左前缀:
... WHERE rental_date> '2005-05-25'DERDER BY rental_date,inventory_id;
但是在这里你可以得到不同的结果,而不是你的SELECT列内容:
首先使用filesort:
EXPLAIN
SELECT `rental_id`, `staff_id` FROM `sakila`.`rental`
WHERE `rental_date` > '2005-05-25'
ORDER BY `rental_date`, `inventory_id`;
输入:ALL
possible_key:rental_date
key:NULL
额外:使用在哪里;使用filesort
第二种情况,使用索引:
EXPLAIN
SELECT `rental_id`, `rental_date`, `inventory_id` FROM `sakila`.`rental`
WHERE `rental_date` > '2005-05-25'
ORDER BY `rental_date`, `inventory_id`;
类型:范围 possible_key:rental_date key:rental_date 额外:使用在哪里;使用索引
为什么它以这种奇怪的方式工作?如前所示,第一个示例使用了索引排序,即使在SELECT子句中包含了带有WHERE子句的不同列。
答案 0 :(得分:0)
在第二个查询中:
SELECT `rental_id`, `rental_date`, `inventory_id` FROM `sakila`.`rental`
WHERE `rental_date` > '2005-05-25'
ORDER BY `rental_date`, `inventory_id`;
MySql直接从索引中检索数据,并且根本不参考该表 请查看索引定义并将其与查询引用的列进行比较:
UNIQUE KEY rental_date (rental_date,inventory_id,customer_id)
索引包含除rental_id
之外的查询引用的所有列,但rental_id
是主键,除了在其定义中明确给出的列之外的每个索引,始终还包含主键值。 />
这是此查询的覆盖索引,请参见此处:http://en.wikipedia.org/wiki/Index_%28database%29#Covering_index
但是在第一个查询中:
SELECT `rental_id`, `staff_id` FROM `sakila`.`rental`
WHERE `rental_date` > '2005-05-25'
ORDER BY `rental_date`, `inventory_id`;
有staff_id
列,未存储在索引中
在这种情况下,MySql必须首先检索与WHERE条件匹配的索引条目,然后对于每个条目必须从表中获取整个记录以获取该条目的缺失staff_id
值。
现在请对您的数据库运行此查询并检查结果:
select count(*) As total,
sum( case when `rental_date` > '2005-05-25' then 1 else 0 end ) As x1,
sum( case when `rental_date` = '2005-05-25' then 1 else 0 end ) As x0
from rental
;
在我的sakila database
副本中,此查询返回以下内容:
+ ---------- + ------- + ------- +
| total | x1 | x0 |
+ ---------- + ------- + ------- +
| 16044 | 16036 | 0 |
+ ---------- + ------- + ------- +
如您所见,表中几乎所有记录(99.9%)都大于2005-05-25
。
在这种情况下,MySql决定不使用索引从表中检索行,但更喜欢将表的全部内容加载到内存中,并在此处进行排序 - 表格相对较小,仅包含16k条记录。
但是,如果还原条件,MySql更喜欢索引访问方法:
EXPLAIN
SELECT `rental_id`, `staff_id` FROM `sakila`.`rental`
WHERE `rental_date` < '2005-05-25'
ORDER BY `rental_date`, `inventory_id`;
+ ------- + ---------------- + ---------- + --------- + ------------------ + -------- + ------------ + -------- + --------- + ---------- +
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+ ------- + ---------------- + ---------- + --------- + ------------------ + -------- + ------------ + -------- + --------- + ---------- +
| 1 | SIMPLE | rental | range | rental_date | rental_date | 5 | | 8 | Using index condition |
+ ------- + ---------------- + ---------- + --------- + ------------------ + -------- + ------------ + -------- + --------- + ---------- +
为什么在第一种情况下不使用索引?因为使用索引条目从表中检索记录通常是最昂贵的方法 - 真的:)
该索引仅在必须检索表的一小部分的情况下是好的 - 几个百分点,可能&lt; 10%。对于从索引中检索的每个索引记录,MySql必须使用它的主键获取一个记录 - 这是一个随机表访问,这比顺序访问慢几倍。
要通过id
从表中只获取一条记录,MySql必须获取包含多条记录的整个页面(块)数据。使用排序索引,我们必须从表中的不同位置逐个获取记录,因此当我们想要使用索引获得90%的表时,会多次检索相同的数据块。在这种情况下,只需按顺序读取一次并在内存中对它们进行排序就更容易,更便宜。