order by优化--Order By实现原理分析和Filesort优化
在MySQL中的ORDER BY有两种排序实现方式:
1、利用有序索引获取有序数据
2、文件排序
在使用explain分析查询的时候,利用有序索引获取有序数据显示Using index。而文件排序显示Using filesort。
1.利用有序索引获取有序数据
取出满足过滤条件作为排序条件的字段,以及可以直接定位到行数据的行指针信息,在 Sort Buffer 中进行实际的排序操作,然后利用排好序的数据根据行指针信息返回表中取得客户端请求的其他字段的数据,再返回给客户端.
这种方式,在使用explain分析查询的时候,显示Using index。而文件排序显示Using filesort。
注意:MySQL在查询时最多只能使用一个索引。因此,如果WHERE条件已经占用了索引,那么在排序中就不使用索引了。
1.1 按照索引对结果进行排序:order by 使用索引是有条件
1) 返回选择的字段,即只包括在有选择的此列上(select后面的字段),不一定适应*的情况):
CREATE TABLE `a20` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`rdate` datetime NOT NULL,
`inventid` int(11) NOT NULL,
`customerid` int(11) NOT NULL,
`staffid` int(11) NOT NULL,
`data` varchar(20) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `rdate`(`rdate`,`inventid`,`customerid`),
KEY `inventid` (`inventid`),
KEY `customerid` (`customerid`),
KEY `staffid` (`staffid`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=latin1;
如:
mysql> explain select inventid from a20 where rdate='2011-12-1400:00:00' order by inventid , customerid;
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+--------------------------+
| 1 | SIMPLE | a20 | NULL | ref | rdate | rdate | 5 | const | 1 | 100.00 | Using where; Using index |
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+--------------------------+
1 row in set (0.00 sec)
2) 只有当ORDER BY中所有的列必须包含在相同的索引,并且索引的顺序和order by子句中的顺序完全一致,并且所有列的排序方向(升序或者降序)一样才有,(混合使用ASC模式和DESC模式则不使用索引)
mysql> explain select inventid from a20 order by rdate,inventid;
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+-------------+
| 1 | SIMPLE | a20 | NULL | index | NULL | rdate | 13 | NULL | 1 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+-------------+
mysql> explain select inventid from a20 order by inventid asc, customerid desc;
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+-----------------------------+
| 1 | SIMPLE | a20 | NULL | index | NULL | rdate | 13 | NULL | 1 | 100.00 | Using index; Using filesort |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+-----------------------------+
3)使用了一个不在索引中的列staff_id
mysql> explain select inventid from a20 where rdate="2011-12-16" order by inventid ,staffid;
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+---------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+---------------------------------------+
| 1 | SIMPLE | a20 | NULL | ref | rdate | rdate | 5 | const | 1 | 100.00 | Using index condition; Using filesort |
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+---------------------------------------+
由于rdate, inventid使用了同一个索引。排序使用到了索引。这个也是满足了前缀索引。但是order by inventid ,staffid;就不是使用了索引,因为staffid和inventid不是同一个索引
mysql> explain select inventid from a20 where rdate="2011-12-16" order by inventid;
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+--------------------------+
| 1 | SIMPLE | a20 | NULL | ref | rdate | rdate | 5 | const | 1 | 100.00 | Using where; Using index |
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+--------------------------+
4) where 语句与ORDER BY语句组合满足最左前缀:
5) 如果查询联接了多个表,只有在order by子句的所有列引用的是第一个表的列才可以。
6) 在其他的情况下,mysql使用文件排序 例如:
1) where语句与order by语句,使用了不同的索引
2) 检查的行数过多,且没有使用覆盖索引
3) ORDER BY中的列不包含在相同的索引,也就是使用了不同的索引
4) 对索引列同时使用了ASC和DESC
5) where语句或者ORDER BY语句中索引列使用了表达式,包括函数表达式
6) where 语句与ORDER BY语句组合满足最左前缀,但where语句中使用了条件查询。查见第10句,虽然where与order by构成了索引最左有缀的条件,但是where子句中使用的是条件查询。
mysql> explain select inventid from a20 where rdate>"2011-12-16";
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | a20 | NULL | index | rdate | rdate | 13 | NULL | 1 | 100.00 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select inventid from a20 where rdate>"2011-12-16" order by inventid;
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+------------------------------------------+
| 1 | SIMPLE | a20 | NULL | index | rdate | rdate | 13 | NULL | 1 | 100.00 | Using where; Using index; Using filesort |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+------------------------------------------+
1 row in set, 1 warning (0.00 sec)
explain select rdate,inventid,customerid,staffid,data from a20;
7) 当使用left join,使用右边的表字段排序
2.文件排序
这个 filesort 并不是说通过磁盘文件进行排序,而只是告诉我们进行了一个排序操作。即在MySQL Query Optimizer 所给出的执行计划(通过 EXPLAIN 命令查看)中被称为文件排序(filesort)
文件排序是通过相应的排序算法,将取得的数据在内存中进行排序: MySQL需要将数据在内存中进行排序,所使用的内存区域也就是我们通过sort_buffer_size 系统变量所设置的排序区。这个排序区是每个Thread 独享的,所以说可能在同一时刻在MySQL 中可能存在多个 sort buffer 内存区域。
在MySQL中filesort 的实现算法实际上是有两种:
双路排序:是首先根据相应的条件取出相应的排序字段和可以直接定位行数据的行指针信息,然后在sort buffer 中进行排序。
单路排序:是一次性取出满足条件行的所有字段,然后在sort buffer中进行排序。
在MySQL4.1版本之前只有第一种排序算法双路排序,第二种算法是从MySQL4.1开始的改进算法,主要目的是为了减少第一次算法中需要两次访问表数据的 IO 操作,将两次变成了一次,但相应也会耗用更多的sortbuffer 空间。当然,MySQL4.1开始的以后所有版本同时也支持第一种算法,
MySQL主要通过比较我们所设定的系统参数 max_length_for_sort_data的大小和Query 语句所取出的字段类型大小总和来判定需要使用哪一种排序算法。如果 max_length_for_sort_data更大,则使用第二种优化后的算法,反之使用第一种算法。所以如果希望 ORDER BY 操作的效率尽可能的高,一定要主义max_length_for_sort_data 参数的设置。曾经就有同事的数据库出现大量的排序等待,造成系统负载很高,而且响应时间变得很长,最后查出正是因为MySQL 使用了传统的第一种排序算法而导致,在加大了max_length_for_sort_data 参数值之后,系统负载马上得到了大的缓解,响应也快了很多。
2.1 MySQL 需要使用filesort 实现排序的实例
假设有 Table A 和 B 两个表结构分别如下:
CREATE TABLE `a` (
`id` int(11) NOT NULL DEFAULT '0',
`c2` int(11) DEFAULT NULL,
`c3` varchar(16) DEFAULT NULL,
`c4` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `b` (
`id` int(11) NOT NULL DEFAULT '0',
`c2` int(11) DEFAULT NULL,
`c3` varchar(16) DEFAULT NULL,
PRIMARY KEY (`id`),
key `b_c2_ind` (`c2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
A.c2不是索引将使用:
mysql> EXPLAIN SELECT A.* FROM A,B WHERE A.id >2 AND A.c2 <5 AND A.c2 = B.c2 ORDER BY A.c2;
+----+-------------+-------+------------+-------+---------------+----------+---------+--------------+------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+--------------+------+----------+-----------------------------+
| 1 | SIMPLE | A | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 1 | 100.00 | Using where; Using filesort |
| 1 | SIMPLE | B | NULL | ref | b_c2_ind | b_c2_ind | 5 | ud_omcs.A.c2 | 1 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+----------+---------+--------------+------+----------+-----------------------------+
MySQL 从 Table A 中取出了符合条件的数据,由于取得的数据并不满足 ORDER BY 条件,所以 MySQL 进行了 filesort 操作,其整个执行过程如下图所示:
2.2 MySQL 需要使用Using temporary 临时表来filesort
如果order by的子句只引用了联接中的第一个表,MySQL会先对第一个表进行排序,然后进行联接。也就是expain中的Extra的Using Filesort.否则MySQL先把结果保存到临时表(Temporary Table),然后再对临时表的数据进行排序.此时expain中的Extra的显示Using temporary Using Filesort.
例如如果我们的排序数据如果是两个(或者更多个) Table 通过 Join所得出的,如下例所示:
mysql> explain select A.* from A,B where A.id > 2 and A.c2 < 5 and A.c2 = B.c2 order by B.c3;
+----+-------------+-------+------------+-------+---------------+----------+---------+--------------+------+----------+----------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+--------------+------+----------+----------------------------------------------+
| 1 | SIMPLE | A | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 1 | 100.00 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | B | NULL | ref | b_c2_ind | b_c2_ind | 5 | ud_omcs.A.c2 | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+----------+---------+--------------+------+----------+----------------------------------------------+
实际执行过程应该是如下图所示:

3. 优化Filesort
当无法避免排序操作时,又该如何来优化呢?很显然,应该尽可能让 MySQL 选择使用第二种单路算法来进行排序。这样可以减少大量的随机IO操作,很大幅度地提高排序工作的效率。
1. 加大 max_length_for_sort_data 参数的设置
在 MySQL 中,决定使用老式排序算法还是改进版排序算法是通过参数 max_length_for_ sort_data 来决定的。当所有返回字段的最大长度小于这个参数值时,MySQL 就会选择改进后的排序算法,反之,则选择老式的算法。所以,如果有充足的内存让MySQL 存放须要返回的非排序字段,就可以加大这个参数的值来让 MySQL 选择使用改进版的排序算法。
2. 去掉不必要的返回字段
当内存不是很充裕时,不能简单地通过强行加大上面的参数来强迫 MySQL 去使用改进版的排序算法,否则可能会造成 MySQL 不得不将数据分成很多段,然后进行排序,这样可能会得不偿失。此时就须要去掉不必要的返回字段,让返回结果长度适应 max_length_for_sort_data 参数的限制。
3. 增大 sort_buffer_size 参数设置
增大 sort_buffer_size 并不是为了让 MySQL选择改进版的排序算法,而是为了让MySQL尽量减少在排序过程中对须要排序的数据进行分段,因为分段会造成 MySQL 不得不使用临时表来进行交换排序。
order by优化--Order By实现原理分析和Filesort优化的更多相关文章
- MySQL Order By实现原理分析和Filesort优化
http://blog.csdn.net/hguisu/article/category/796963
- Solr缓存原理分析及配置优化
一.缓存原理 缓存,带来急速性能体验! Solr提供了一系列的内置缓存来优化查询性能.Solr的缓存原理主要涉及以下4个方面: 1.缓存大小及缓存置换法 从缓存大小的角度来看,不能将缓存设置的太大,否 ...
- 【MySQL 原理分析】之 Trace 分析 order by 的索引原理
一.背景 昨天早上,交流群有一位同学提出了一个问题.看下图: 我不是大佬,而且当时我自己的想法也只是猜测,所以并没有回复那位同学,只是接下来自己做了一个测试验证一下. 他只简单了说了一句话,就是同样的 ...
- 优化order by 语句
mysql 演示数据库:http://downloads.mysql.com/docs/sakila-db.zip mysql 中排序方式 有序索引顺序扫描直接返回有序数据 explain selec ...
- MySQL——优化ORDER BY语句
本篇文章我们将了解ORDER BY语句的优化,在此之前,你需要对索引有基本的了解,不了解的朋友们可以先看一下我之前写过的索引相关文章.现在让我们开始吧. MySQL中的两种排序方式 1.通过有序索引顺 ...
- 8.2.1.15 ORDER BY Optimization ORDER BY 优化
8.2.1.15 ORDER BY Optimization ORDER BY 优化 在一些情况下, MySQL 可以使用一个索引来满足一个ORDER BY 子句不需要做额外的排序 index 可以用 ...
- mysql索引优化-order/group
为排序使用索引 KEY a_b_c (a,b,c) order by 能使用索引最左前缀 -order by a -order by a,b -order by a,b,c -order by a d ...
- MySQL实验 内连接优化order by+limit 以及添加索引再次改进
MySQL实验 内连接优化order by+limit 以及添加索引再次改进 在进行子查询优化双参数limit时我萌生了测试更加符合实际生产需要的ORDER BY + LIMIT的想法,或许我们也可以 ...
- ConcurrentHashMap原理分析(1.7与1.8)-put和 get 需要执行两次Hash
ConcurrentHashMap 与HashMap和Hashtable 最大的不同在于:put和 get 两次Hash到达指定的HashEntry,第一次hash到达Segment,第二次到达Seg ...
随机推荐
- Js 获取当前时间
Js获取当前日期时间及其它操作 var myDate = new Date(); myDate.getYear(); //获取当前年份(2位) myDate.getFullYear(); //获取完整 ...
- 【BZOJ 2321】 [BeiJing2011集训]星器
Description Magic Land上的时间又过了若干世纪…… 现在,人们谈论着一个传说:从前,他们的祖先来到了一个位于东方的岛屿,那里简直就是另外一个世界.善于分析与构造的Magic Lan ...
- UITableView实现格瓦拉飞天投票模块-b
格瓦拉目前来说动画效果确实做的还比较好,虽然不是说很炫但做到精致,这次就模仿了它投票的模块.其实想到要实现它还是有很多方法,不过这次我还是采用了苹果自带控件UITableView简简单单来实现它,再次 ...
- DXperience-12.1.5 官网下载+注册破解+帮助文档
安装包 DXperience 12.1.5 Universal 帮助文档: DXperienceHelp2010 DXperienceHelp2010-12.1.5.exe DXperienceHel ...
- 在SpringMVC利用MockMvc进行单元测试
spring在线文档:https://docs.spring.io/spring/docs/current/javadoc-api/index.html?index-files/index-13.ht ...
- Matlab实现ItemBasedCF算法
感谢开源大神,慢慢收集资料.网上资料很少,我在找如何用matlab处理movielens数据集时找到的. 用ml-100k这个数据集,包括主函数和相似度函数. 代码托管于CSDN.
- 8 行 Node.js 代码实现代理服务器
接触 Node.js 已有多年,一直喜欢它的单线程模型和异步IO特性,以及 JavaScript 语言本身的灵活性.同时,JavaScript 前后端通吃,在全栈开发领域具有独特的优势.今天就来看看作 ...
- POJ 3786 Adjacent Bit Counts (DP)
点我看题目 题意 :给你一串由1和0组成的长度为n的数串a1,a2,a3,a4.....an,定义一个操作为AdjBC(a) = a1*a2+a2*a3+a3*a4+....+an-1*an.输入两个 ...
- 关于PYTHON的反射,装饰的练习
从基本概念,简单例子才能慢慢走到高级一点的地方. 另外,PYTHON的函数式编程也是我很感兴趣的一点. 总体而言,我觉得OOP可以作大的框架和思路,FP能作细节实现时的优雅牛X. ~~~~~~~~~~ ...
- live555源码研究(四)------UserAuthenticationDatabase类
一.UserAuthenticationDatabase类作用 1,用户/密码管理 2,鉴权管理 二.类UserAuthenticationDatabase继承关系图 ...