在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后面的字段),不一定适应*的情况):

  1. CREATE TABLE `a20` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT,
  3. `rdate` datetime NOT NULL,
  4. `inventid` int(11) NOT NULL,
  5. `customerid` int(11) NOT NULL,
  6. `staffid` int(11) NOT NULL,
  7. `data` varchar(20) NOT NULL,
  8. PRIMARY KEY (`id`),
  9. UNIQUE KEY `rdate`(`rdate`,`inventid`,`customerid`),
  10. KEY `inventid` (`inventid`),
  11. KEY `customerid` (`customerid`),
  12. KEY `staffid` (`staffid`)
  13. ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=latin1;

如:

  1. mysql> explain select inventid from a20 where rdate='2011-12-1400:00:00' order by inventid , customerid;
  2. +----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+--------------------------+
  3. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  4. +----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+--------------------------+
  5. | 1 | SIMPLE | a20 | NULL | ref | rdate | rdate | 5 | const | 1 | 100.00 | Using where; Using index |
  6. +----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+--------------------------+
  7. 1 row in set (0.00 sec)

2) 只有当ORDER BY中所有的列必须包含在相同的索引,并且索引的顺序和order by子句中的顺序完全一致,并且所有列的排序方向(升序或者降序)一样才有,(混合使用ASC模式和DESC模式则不使用索引)

  1. mysql> explain select inventid from a20 order by rdate,inventid;
  2. +----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+-------------+
  3. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  4. +----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+-------------+
  5. | 1 | SIMPLE | a20 | NULL | index | NULL | rdate | 13 | NULL | 1 | 100.00 | Using index |
  6. +----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+-------------+
  7. mysql> explain select inventid from a20 order by inventid asc, customerid desc;
  8. +----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+-----------------------------+
  9. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  10. +----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+-----------------------------+
  11. | 1 | SIMPLE | a20 | NULL | index | NULL | rdate | 13 | NULL | 1 | 100.00 | Using index; Using filesort |
  12. +----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+-----------------------------+

3)使用了一个不在索引中的列staff_id

  1. mysql> explain select inventid from a20 where rdate="2011-12-16" order by inventid ,staffid;
  2. +----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+---------------------------------------+
  3. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  4. +----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+---------------------------------------+
  5. | 1 | SIMPLE | a20 | NULL | ref | rdate | rdate | 5 | const | 1 | 100.00 | Using index condition; Using filesort |
  6. +----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+---------------------------------------+

由于rdate, inventid使用了同一个索引。排序使用到了索引。这个也是满足了前缀索引。但是order by inventid ,staffid;就不是使用了索引,因为staffid和inventid不是同一个索引

  1. mysql> explain select inventid from a20 where rdate="2011-12-16" order by inventid;
  2. +----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+--------------------------+
  3. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  4. +----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+--------------------------+
  5. | 1 | SIMPLE | a20 | NULL | ref | rdate | rdate | 5 | const | 1 | 100.00 | Using where; Using index |
  6. +----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+--------------------------+

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子句中使用的是条件查询。

  1. mysql> explain select inventid from a20 where rdate>"2011-12-16";
  2. +----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
  3. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  4. +----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
  5. | 1 | SIMPLE | a20 | NULL | index | rdate | rdate | 13 | NULL | 1 | 100.00 | Using where; Using index |
  6. +----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
  7. 1 row in set, 1 warning (0.00 sec)
  1. mysql> explain select inventid from a20 where rdate>"2011-12-16" order by inventid;
  2. +----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+------------------------------------------+
  3. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  4. +----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+------------------------------------------+
  5. | 1 | SIMPLE | a20 | NULL | index | rdate | rdate | 13 | NULL | 1 | 100.00 | Using where; Using index; Using filesort |
  6. +----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+------------------------------------------+
  7. 1 row in set, 1 warning (0.00 sec)
  8. 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不是索引将使用:

  1. mysql> EXPLAIN SELECT A.* FROM A,B WHERE A.id >2 AND A.c2 <5 AND A.c2 = B.c2 ORDER BY A.c2;
  2. +----+-------------+-------+------------+-------+---------------+----------+---------+--------------+------+----------+-----------------------------+
  3. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  4. +----+-------------+-------+------------+-------+---------------+----------+---------+--------------+------+----------+-----------------------------+
  5. | 1 | SIMPLE | A | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 1 | 100.00 | Using where; Using filesort |
  6. | 1 | SIMPLE | B | NULL | ref | b_c2_ind | b_c2_ind | 5 | ud_omcs.A.c2 | 1 | 100.00 | Using index |
  7. +----+-------------+-------+------------+-------+---------------+----------+---------+--------------+------+----------+-----------------------------+

MySQL 从 Table A 中取出了符合条件的数据,由于取得的数据并不满足 ORDER BY 条件,所以 MySQL 进行了 filesort 操作,其整个执行过程如下图所示:

2.2 MySQL 需要使用Using temporary 临时表来filesort

临时表见《MySQL在以下几种情况会创建临时表
如果order by的子句只引用了联接中的第一个表,MySQL会先对第一个表进行排序,然后进行联接。也就是expain中的Extra的Using Filesort.否则MySQL先把结果保存到临时表(Temporary Table),然后再对临时表的数据进行排序.此时expain中的Extra的显示Using temporary Using Filesort.

例如如果我们的排序数据如果是两个(或者更多个) Table 通过 Join所得出的,如下例所示:

  1. mysql> explain select A.* from A,B where A.id > 2 and A.c2 < 5 and A.c2 = B.c2 order by B.c3;
  2. +----+-------------+-------+------------+-------+---------------+----------+---------+--------------+------+----------+----------------------------------------------+
  3. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  4. +----+-------------+-------+------------+-------+---------------+----------+---------+--------------+------+----------+----------------------------------------------+
  5. | 1 | SIMPLE | A | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 1 | 100.00 | Using where; Using temporary; Using filesort |
  6. | 1 | SIMPLE | B | NULL | ref | b_c2_ind | b_c2_ind | 5 | ud_omcs.A.c2 | 1 | 100.00 | NULL |
  7. +----+-------------+-------+------------+-------+---------------+----------+---------+--------------+------+----------+----------------------------------------------+

实际执行过程应该是如下图所示:

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 不得不使用临时表来进行交换排序。

mysql实战优化之六:Order by优化 sql优化、索引优化的更多相关文章

  1. MS SQL SERVER索引优化相关查询

        查找缺失索引 -- =============================================   -- Description: 查询当前数据库中缺失的索引,知道你进行优化的 ...

  2. SQL 查询优化 索引优化

    sql语句优化 性能不理想的系统中除了一部分是因为应用程序的负载确实超过了服务器的实际处理能力外,更多的是因为系统存在大量的SQL语句需要优化. 为了获得稳定的执行性能,SQL语句越简单越好.对复杂的 ...

  3. Mysql数据库优化之SQL及索引优化

    1. 如何发现有问题的SQL?  使用mysql慢查询日志对有效率问题的Sql进行监视 (1) show  variables like 'slow_query_log';     查看慢查询日志是否 ...

  4. Mysql 性能优化7【重要】sql语句的优化 浅谈MySQL中优化sql语句查询常用的30种方法(转)

    原文链接   http://www.jb51.net/article/39221.htm 这篇文章大家都在转载,估计写的有条理吧,本人稍微做一下补充 1.对查询进行优化,应尽量避免全表扫描,首先应考虑 ...

  5. Mysql 性能优化7【重要】sql语句的优化 慢查询

    慢查询时间设置 慢查询日志分析工具 另一个慢查询日志分析工具 如何对sql进行特定的优化

  6. Mysql中Innodb大量插入数据时SQL语句的优化

    innodb优化后,29小时入库1300万条数据 参考:http://blog.51yip.com/mysql/1369.html 对于Myisam类型的表,可以通过以下方式快速的导入大量的数据: A ...

  7. Mysql(MyISAM和InnoDB)及Btree和索引优化

    MYSQL 一.引擎 mysql:MySQL是一个关系型数据库管理系统,其中有两种引擎最为常见MyISAM和InnoDB MyISAM(非聚集索引)  MySQL 5.0 之前的默认数据库引擎,最为常 ...

  8. SQL优化技术分析-3:SQL语句索引的利用

    使用索引来更快地遍历表.默认情况下建立的索引是非聚集索引,但有时它并不是最佳的.在非聚集索引下,数据 在物理上随机存放在数据页上.合理的索引设计要建立在对各种查询的分析和预测上.一般来说: 有大量重复 ...

  9. SQL Server 索引优化-----数据库引擎优化顾问

    本文将根据“数据库引擎优化顾问”(DTA)来发现无用或缺失的索引. 要使用“数据库引擎优化顾问”,首先需要对数据库负载进行监控,为数据库负载分析准备数据.从SSMS的工具中,打开SQL Server  ...

  10. SQL Server 索引优化——无用索引

    我们知道,合理的索引能大幅提升性能,但冗余的索引也会降低数据库性能.随着我们业务的发展,数据库的中的表.表结构.查询的内容都有可能发生变化.这样,有的索引就可能不再使用了,需要删除(因为维护索引即浪费 ...

随机推荐

  1. git branch 常用语句详解

    删除远程分支 git push origin --delete <branchName> 查看本地分支 git branch 创建本地新分支 git branch <branchNa ...

  2. JNI_Z_04_属性的操作(非String类型的属性)

    1.步骤 : (1).获取 jclass (2).获取 类属性字段的id(最后一个参数是 属性字段 的签名) (3).获取/设置 类属性字段的值 ZC: 貌似 JNI里面 操作 类属性字段,完全是 无 ...

  3. LeetCode第[36]题(Java):Valid Sudoku

    题目:有效的数独表 难度:Medium 题目内容: Determine if a 9x9 Sudoku board is valid. Only the filled cells need to be ...

  4. [JavaScript]对象创建方法

    1.使用Object或对象字面量创建对象 (1)使用Object创建对象 var cat= new Object(); cat.name = "Tom"; cat.color= & ...

  5. Xcode 8 GM 编译缺失 /Users/usr/lib/libresolv.9.dylib

    原因是操作系统的文件与手机需要的不同. 解决办法是将iOS DeviceSupport里当前手机版本的Symbols的libresolv.9.dylib文件,代替编译失败项目的Build Phases ...

  6. BCCoventUtils全角与半角互相转换

    public class BCConvert { /** * ASCII表中可见字符从!开始,偏移位值为33(Decimal) */ static final char DBC_CHAR_START ...

  7. How to Fix “ShellExecute failed (2): Is this command correct?” on Notepad++

    Problem: When you click right-click->Edit with Notepad ++ and get the error “ShellExecute failed ...

  8. Java中的深拷贝(深复制)和浅拷贝(浅复制)

    深拷贝(深复制)和浅拷贝(浅复制)是两个比较通用的概念,尤其在C++语言中,若不弄懂,则会在delete的时候出问题,但是我们在这幸好用的是Java.虽然java自动管理对象的回收,但对于深拷贝(深复 ...

  9. 剑指offer--32.把数组排成最小的数

    用to_string()将整形转化为字符串,对字符串进行比较 --------------------------------------------------------------------- ...

  10. Slice header里面有哪些重要的参数?

    first_mb_in_slice:第一个宏块在slice的位置 slice_type:slice的类型 pic_parameter_set_id:slice对应的pps的id frame_num:表 ...