mysql有两种方式可以生成有序的结果,通过排序操作或者按照索引顺序扫描,如果explain的type列的值为index,则说明mysql使用了索引扫描来做排序(不要和extra列的Using index搞混了,那个是使用了覆盖索引查询)。扫描索引本身是很快的,因为只需要从一条索引记录移动到紧接着的下一条记录,但如果索引不能覆盖查询所需的全部列,那就不得不扫描一条索引记录就回表查询一次对应的整行,这基本上都是随机IO,因此按索引顺序读取数据的速度通常要比顺序地全表扫描慢,尤其是在IO密集型的工作负载时。

  mysql可以使用同一个索引既满足排序,又用于查找行,因此,如果可能,设计索引时应该尽可能地同时满足这两种任务,这样是最好的。只有当索引的列顺序和order by子句的顺序完全一致,并且所有列的排序方向(倒序或升序,创建索引时可以指定ASC或DESC)都一样时,mysql才能使用索引来对结果做排序,如果查询需要关联多张表,则只有当order by子句引用的字段全部为第一个表时,才能使用索引做排序,order by子句和查找型查询的限制是一样的,需要满足索引的最左前缀的要求,否则mysql都需要执行排序操作,而无法使用索引排序。

示例:

有表rental,表结构如下:

mysql > CREATE TABLE `rental` (
  `rental_id` int(11) NOT NULL AUTO_INCREMENT,
  `rental_date` datetime NOT NULL,
  `inventory_id` mediumint(8) unsigned NOT NULL,
  `customer_id` smallint(5) unsigned NOT NULL,
  `return_date` datetime DEFAULT NULL,
  `staff_id` tinyint(3) unsigned NOT NULL,
  `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  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`),
  CONSTRAINT `fk_rental_customer` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`customer_id`) ON UPDATE CASCADE,
  CONSTRAINT `fk_rental_inventory` FOREIGN KEY (`inventory_id`) REFERENCES `inventory` (`inventory_id`) ON UPDATE CASCADE,
  CONSTRAINT `fk_rental_staff` FOREIGN KEY (`staff_id`) REFERENCES `staff` (`staff_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=16050 DEFAULT CHARSET=utf8;
 
表数据量如下:
mysql > select count(*) from rental;
+----------+
| count(*) |
+----------+
|    16044 |
+----------+
1 row in set (0.04 sec)
 
索引信息如下: 
mysql > show index from rental;
 

mysql可以使用rental_date索引为下面的SQL查询做排序,从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 ***************************

id: 1

select_type: SIMPLE

table: rental

type: ref

possible_keys: rental_date

key: rental_date

key_len: 5

ref: const

rows: 1

Extra: Using where

1 row in set (0.01 sec)

注意:where条件列可以不按照索引定义的顺序出现,不管按照什么顺序出现索引列,只要出现的索引列在索引定义顺序的列上能连起来就行,但是order by列不同,出现顺序一定得按照索引定义的顺序,否则无法使用索引进行排序,如,把inventory_id和costomer_id交换一下,就会出现filesort,因为索引是按照定义时的顺序排序,order by列打乱这个排序顺序就无法使用索引进行排序了

mysql > explain select rental_id,staff_id from sakila.rental where rental_date = '2005-05-25' order by customer_id,inventory_id\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: rental

type: ref

possible_keys: rental_date

key: rental_date

key_len: 5

ref: const

rows: 1

Extra: Using where; Using filesort

1 row in set (0.00 sec)

从上面示例中可以看到,order by子句不满足索引的最左前缀要求(rental_date列没有出现,只出现了索引的后边两列),不能用rental_date来排序,但可以用于rental_date列查询,这是因为索引的第一个列被指定为常数。

还有一些可以使用索引排序的查询示例:

下面这个索引第一列在where上,第二列在order by

mysql > explain select rental_id,staff_id from sakila.rental where rental_date='2005-05-05' order by inventory_id desc\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: rental

type: ref

possible_keys: rental_date

key: rental_date

key_len: 5

ref: const

rows: 1

Extra: Using where

1 row in set (0.00 sec)

下面这个查询因为order by使用的两列就是索引的最左前缀

mysql > explain select rental_id,staff_id from sakila.rental where rental_date='2005-05-05' order by rental_date,inventory_id\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: rental

type: ref

possible_keys: rental_date

key: rental_date

key_len: 5

ref: const

rows: 1

Extra: Using where

1 row in set (0.00 sec)

下面是一些不能使用索引做排序的查询:

下面这个查询使用了两种不同的排序方向,但是索引列最左前缀是符合的

mysql > explain select rental_id,staff_id from sakila.rental where rental_date='2005-05--25' rder by inventory_id desc,customer_id asc\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: rental

type: ref

possible_keys: rental_date

key: rental_date

key_len: 5

ref: const

rows: 1

Extra: Using where; Using filesort

1 row in set (0.00 sec)

下面这个查询的order by子句中使用了一个不在索引中的列

mysql > explain select rental_id,staff_id from sakila.rental where rental_date='2005-05--25' order by inventory_id,staff_id\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: rental

type: ref

possible_keys: rental_date

key: rental_date

key_len: 5

ref: const

rows: 1

Extra: Using where; Using filesort

1 row in set (0.00 sec)

下面这个查询的where和order by中的列无法组合成索引的最左前缀,中间缺失了inventory_id列:

mysql > explain select rental_id,staff_id from sakila.rental where rental_date='2005-05-25' order by customer_id\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: rental

type: ref

possible_keys: rental_date

key: rental_date

key_len: 5

ref: const

rows: 1

Extra: Using where; Using filesort

1 row in set (0.00 sec)

下面这个查询在索引列的第一列上是范围查询,所以mysql无法使用索引的其余列,这个范围条件后边的无论是查询条件列还是排序列都无法使用到索引:

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 ***************************

id: 1

select_type: SIMPLE

table: rental

type: ALL

possible_keys: rental_date

key: NULL

key_len: NULL

ref: NULL

rows: 16005

Extra: Using where; Using filesort

1 row in set (0.00 sec)

面这个查询在inventory_id列上有多个等于条件,对于排序来说,这也是一种范围查询,inventory_id条件列后面的无论是查询还是排序都无法使用索引:

mysql > explain select rental_id,staff_id from sakila.rental where rental_date=2005-05-25 and inventory_id in (1,2) order by customer_id\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: rental

type: ref

possible_keys: rental_date,idx_fk_inventory_id

key: rental_date

key_len: 5

ref: const

rows: 1

Extra: Using index condition; Using where; Using filesort

1 row in set, 5 warnings (0.00 sec)

下面这个查询理论上是可以使用索引进行关联排序的,但是由于优化器在优化时将film_actor表当作关联的第二个表,所以实际上无法使用索引,即排序列不是驱动表的列就无法使用索引排序:

mysql > explain select actor_id,title from sakila.film_actor join sakila.film using(film_id) order by actor_id\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: film

type: index

possible_keys: PRIMARY

key: idx_title

key_len: 767

ref: NULL

rows: 1000

Extra: Using index; Using temporary; Using filesort

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: film_actor

type: ref

possible_keys: idx_fk_film_id

key: idx_fk_film_id

key_len: 2

ref: sakila.film.film_id

rows: 2

Extra: Using index

2 rows in set (0.00 sec)

可以使用straight_join语句指定关联表的顺序:

mysql > explain select actor_id,title from sakila.film_actor as a straight_join sakila.film as b on a.film_id=b.film_id order by actor_id\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: a

type: index

possible_keys: idx_fk_film_id

key: PRIMARY

key_len: 4

ref: NULL

rows: 5462

Extra: Using index

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: b

type: eq_ref

possible_keys: PRIMARY

key: PRIMARY

key_len: 2

ref: sakila.a.film_id

rows: 1

Extra: NULL

2 rows in set (0.00 sec)

  要注意:straight_join属于非标准的语法,在mysql优化器能做出正确选择的时候就尽量不要使用,只有在mysql优化器做出错误的选择时才使用它,straight_join是一种hit提示关键字,使用straight_join关键字时,如果只有两个表关联要就只使用straight_join就可以了,不需要再去指定join,left join,right join等关键字,否则会报1066表或表别名重复的错误,另外,使用straight_join后指定关联表的关联字段时发现使用using(xx)报语法错,改使用on a.xx=b.xx形式指定就不报错,不知道是不是使用straight_join关键字时不支持using指定关联字段

关于straight_join提示:

这个提示可放置在select关键字之后,也可以放置在任何两个关联表的表名之前,第一个用法是让查询中所有的表按照在语句中出现的顺序进行关联,第二个用法则是固定其前后两个表的关联顺序。当mysql没正确选择关联顺序的时候,或者由于可能的顺序太多导致mysql无法评估所有的关联顺序的时候,straight_join都会很有用,如果关联表可能的顺序太多,可能导致mysql花费大量时间在statistics状态。可以使用explain语句来查看关联顺序,然后加上这个提示再用explain查看有没有变化。

mysql使用索引扫描来做排序的更多相关文章

  1. MySQL 使用索引扫描来做排序

    MySQL有两种方式可以生成有序的结果:通过排序操作:或者按照索引顺序扫描:如果EXPLAIN 出来的结果的type列的值为“index”,则说明MySQL使用了索引扫描来做排序(不要和Extra列的 ...

  2. MySQL松散索引扫描与紧凑索引扫描

    什么是松散索引? 答:实际上就是当MySQL 完全利用索引扫描来实现GROUP BY 的时候,并不需要扫描所有满足条件的索引键即可完成操作得出结果. 要利用到松散索引扫描实现GROUP BY,需要至少 ...

  3. MySQL之索引(三)

    聚簇索引 聚簇索引并不是一种单独的索引类型,而是一种数据存储方式.具体的细节依赖于其实现方式,但InnoDB的聚簇索引实际上在同一个结构中保存了B-Tree索引和数据行.当表有聚簇索引时,它的数据行实 ...

  4. 高性能mysql——高性能索引策略

    <高性能MySQL>读书笔记 一. 索引的优点 1. 索引可以让服务器快速定位到表的指定位置,大大减少了服务器需要扫描的数量: 2. 最常见的B-Tree索引按照顺序存储数据,可以用来做o ...

  5. MySQL的索引优化,查询优化

    MySQL逻辑架构 如果能在头脑中构建一幅MySQL各组件之间如何协同工作的架构图,有助于深入理解MySQL服务器.下图展示了MySQL的逻辑架构图. MySQL逻辑架构,来自:高性能MySQL My ...

  6. MySQL优化 - 索引优化

    索引(在MySQL中也叫做"键(key)")是存储引擎用于快速找到记录的一种数据结构. 索引对于良好的性能非常关键,尤其是当表的数据量越来越大时,索引对性能(查询)的影响愈发重要. ...

  7. MySQL优化GROUP BY-松散索引扫描与紧凑索引扫描

    满足GROUP BY子句的最一般的方法是扫描整个表并创建一个新的临时表,表中每个组的所有行应为连续的,然后使用该临时表来找到组并应用累积函数(如果有).在某些情况中,MySQL能够做得更好,即通过索引 ...

  8. MySQL如何利用索引优化ORDER BY排序语句

    MySQL索引通常是被用于提高WHERE条件的数据行匹配或者执行联结操作时匹配其它表的数据行的搜索速度. MySQL也能利用索引来快速地执行ORDER BY和GROUP BY语句的排序和分组操作. 通 ...

  9. MySQL如何利用索引优化ORDER BY排序语句 【转载】

    本文转载自:http://blog.csdn.net/ryb7899/article/details/5580624  .感谢相关作者. MySQL索引通常是被用于提高WHERE条件的数据行匹配或者执 ...

随机推荐

  1. 【关于HBITMAP, DC, MEM DC, Clipboard】将HBITMAP拷贝到Clipboard(Windows Clipboard & OLE Clipboard)

    参考: Programming Windows with MFC, 2nd. Chapter 18, 19. 建议把这两章学习完(至少到OLE drag-and-drop之前要学习完)再来尝试OLE ...

  2. 使用IXmlSerializable的问题

    最近又开始使用XML了,但今天遇到一个折腾我一下午加一个晚上的时间,终于从网络上找到相关的资料解决了. 有一个成员是用来存放正则表达式的,由于里面包含其它字符,所以想用CDATA来保存方便查看,所以想 ...

  3. Java基础之读文件——使用通道读二进制数据(ReadPrimes)

    控制台程序,本例读取Java基础之写文件部分(PrimesToFile)写入的primes.bin. import java.nio.file.*; import java.nio.*; import ...

  4. java 笔记(3) —— 动态代理,静态代理,cglib代理

    0.代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口. 代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 代理类与委托类之间通常会存 ...

  5. 从零开始攻略PHP(6)——代码重用与函数编写的一些注意事项

    一个新的项目是这样创建的:它将已有的可重新利用的组件进行组合,并将新的开发难度降低到最小. 代码重用的好处:降低成本.提升可靠性和一致性. 1.使用require()和include()函数 使用一条 ...

  6. 最大权闭合图最大获益(把边抽象为点)HDU3879

    题意:给出一个无向图,每个点都有点权值代表花费,每条边都有利益值,代表形成这条边就可以获得e[i]的利益,问选择那些点可以获得最大利益是多少? 分析:把边抽象成点,s与该点建边,容量是利益值,每个点与 ...

  7. zoj The 12th Zhejiang Provincial Collegiate Programming Contest Team Formation

    http://acm.zju.edu.cn/onlinejudge/showContestProblem.do?problemId=5494 The 12th Zhejiang Provincial ...

  8. [原创]java WEB学习笔记68:Struts2 学习之路-- 类型转换与复杂属性配合使用

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  9. fences(桌面整理软件)与eDiary3.3.3下载链接

    fences:  http://www.jb51.net/softs/309746.html http://jingyan.baidu.com/article/e8cdb32b6e958337042b ...

  10. Java之Structs框架初探

    今天是小白第一次自己的接触Struts框架,因为网上的资料都是从Structs2开始,跟Structs1完全不同,因此,小白直接跳过1学习版本2的搭建,废话不多说,直接开始. 首先要搭建框架,就肯定要 ...