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. bootstrap学习之一_bootstrap css

    一.文本相关样式 文本格式(用于块状标签):text-left:向左对齐文本:text-center:居中对齐文本:text-right:向右对齐文本:text-justified;text-nowr ...

  2. ADB 在 Android SDK 的中的路径

    以前 adb.exe 是在 sdk/tools 目录下 现在 安装 sdk 之后, 需要打开 SDK Manager 下载 `Android SDK Platform-tools` 然后, 在 sdk ...

  3. 淘宝付邮试用Chrome桌面提醒插件(含源码)

    淘宝付邮试用每天都会推出10元邮费试用的商品,有些对自己还是有用的.试用一下还不错,关键是便宜.不过,有些商家也抓住了买家贪便宜的特点,推出廉价垃圾商品.这里,大家还是慧眼识别了.每天浏览这些商品会很 ...

  4. CentOS 学习笔记

    整理基础的CentOS常用命令 http://os.51cto.com/art/201003/190801.htm 在Hyper-V中的CentOS虚拟机中使用网络 http://blog.earth ...

  5. Android的LinearLayout中的权重android:layout_weight

    当前EditText和Button部件只是适应了他们各自内容的大小,如下图所示: 这样设置对按钮来说很合适,但是对于文本框来说就不太好了,因为用户可能输入更长的文本内容.因此如果能够占满整个屏幕宽度会 ...

  6. The Struts dispatcher cannot be found. This is usually caused by using Strut

    The Struts dispatcher cannot be found. This is usually caused by using Struts tags without the assoc ...

  7. eclipse启动无响应,停留在Loading workbench状态

    做开发的同学们或多或少的都会遇到eclipse启动到一定程度时,就进入灰色无响应状态再也不动了.启动画面始终停留在Loading workbench状态.反复重启,状态依旧. 多数情况下,应该是非正常 ...

  8. SqlServer StringToTable性能测试

    问题起因: 最近做的项目DB数据量比较大(基本上一个月的数据就是10亿),而工程中Proc参数中包含有id拼接字符串,id拼接字符串格式:1,2,4,5,100,301.当数据量很小的情况下,这样做没 ...

  9. redhat vim编辑器永久添加行号

    cd ~ vim .vimrc 第一行加入: set nu :wq 保存退出,即可 如果想取消设置,同理删除set nu即可

  10. Effective C++ 1.让自己习惯C++

    //条款01:视C++为一个语言联邦 // 1:C++主要包含的语言为: // A:C.说到底C++仍然以C为基础.区块(blocks).语句.预处理器.内置数据类型.数组.指针等均来自于C.许多时候 ...