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

  1. CREATETABLE `test` (


  2. `id` int(11) NOT NULLAUTO_INCREMENT,

  3. `rdate` datetime NOTNULL,

  4. `inventid` int(11) NOTNULL,

  5. `customerid` int(11) NOTNULL,

  6. `staffid` int(11) NOTNULL,

  7. `data` varchar(20) NOTNULL,

  8. PRIMARYKEY (`id`),

  9. UNIQUEKEY `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>


  2. explain select inventid from test where rdate=‘2011-12-1400:00:00‘orderby inventid ,
    customerid;

+----+-------------+-------+------+---------------+-------+---------+-------+------+--------------------------+

| id | select_type | table
| type | possible_keys |key | key_len
|ref | rows |Extra |

+----+-------------+-------+------+---------------+-------+---------+-------+------+--------------------------+

| 1 | SIMPLE | test |ref | rdate
|rdate | 8 |const | 10 | Using
where; Using index|

+----+-------------+-------+------+---------------+-------+---------+-------+------+--------------------------+

1 row in set (0.00 sec)

Select选择的列使用索引,而下面不使用索引:

mysql> explain select * from test where rdate=‘2011-12-14 00:00:00‘order
by inventid , customerid ;

+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+

| id | select_type | table
| type | possible_keys | key |
key_len|ref | rows | Extra |

+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+

| 1 | SIMPLE | test | ALL |
rdate | NULL | NULL |NULL | 13
|Using where;Using filesort|

+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+

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

  1. mysql>


  2. xplain select inventid from test order byrdate, inventid
    ;

+----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+

| id | select_type | table
| type | possible_keys | key | key_len | ref | rows |
Extra |

+----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+

| 1 | SIMPLE | test | index | NULL |
rdate |16 | NULL | 13 |Using index|

+----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+

1 row in set (0.00 sec)

  1. mysql>


  2. explain select inventid from test where rdate="2011-12-16"orderby inventid
    ,staffid;

+----+-------------+-------+------+---------------+-------+---------+-------+------+--------------------------

| id | select_type | table | type | possible_keys |key | key_len | ref | rows
| Extra |

+----+-------------+-------+------+---------------+-------+---------+-------+------+--------------------------

| 1 | SIMPLE | test | ref | rdate | rdate | 8 | const | 1 |Using
where;Using filesort

+----+-------------+-------+------+---------------+-------+---------+-------+------+--------------------------

1 row in set (0.00 sec)

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

3) where 语句与ORDER BY语句组合满足最左前缀:

  1. mysql>


  2. explain select inventid from test
    whererdate="2011-12-16"orderby inventid
    ;

+----+-------------+-------+------+---------------+-------+---------+-------+------+--------------------------+

| id | select_type | table | type | possible_keys | key | key_len | ref |
rows | Extra |

+----+-------------+-------+------+---------------+-------+---------+-------+------+--------------------------+

| 1 | SIMPLE | test | ref | rdate | rdate | 8 | const | 1 | Using
where;Using index |

+----+-------------+-------+------+---------------+-------+---------+-------+------+--------------------------+

1 row in set (0.00 sec)

4) 如果查询联接了多个表,只有在order by子句的所有列引用的是第一个表的列才可以。

5) 在其他的情况下,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 test where
rdate>"2011-12-16"
 order by inventid;

+----+-------------+-------+-------+---------------+-------+---------+------+------+----------------

| id | select_type | table | type | possible_keys | key | key_len | ref |
rows | Extra

+----+-------------+-------+-------+---------------+-------+---------+------+------+----------------

| 1 |SIMPLE | test | range | rdate | rdate | 8 | NULL | 1 | Using
where; Using
index;
Usingfilesort |

+----+-------------+-------+-------+---------------+-------+---------+------+------+----------------

1 row in set (0.00sec)

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 两个表结构分别如下:




# mysql

>show create table
A\G
  *************************** 1. row
***************************
  Table: A
  Create Table: CREATE TABLE
`A` (
  `id` int(11) NOT NULL default ‘0‘,
  `c2`
char(2) default NULL,
  `c3`
varchar(16) default NULL, 
  `c4` datetime
default NULL, 
  PRIMARY KEY (`id`) 
  ) ENGINE=MyISAM
DEFAULT CHARSET=utf8

#:mysql

> show create table
B\G
  *************************** 1. row
***************************   Table: B
  Create Table: CREATE TABLE `B`

  `id` int(11) NOT NULL default
‘0‘, 
  `c2` char(2) default NULL,
  `c3`
varchar(16) default NULL, 
  PRIMARY KEY
(`id`),

  KEY `B_c2_ind` (`c2`)
  ) ENGINE=MyISAM DEFAULT
CHARSET=utf8

A.c2不是索引将使用:




[email protected] :
example 01:54:23>
EXPLAIN SELECT A.* FROM A,B WHERE A.id >2 AND
A.c2 <5 AND A.c2 = B.c2 ORDER BY
A.c2\G

  *************************** 1. row
***************************
  id: 1
  select_type:
SIMPLE
  table: A
  type: range
  possible_keys:
PRIMARY
  key: PRIMARY
  key_len: 4
  ref:
NULL
  rows: 3
  Extra: Using where; Using
filesort

*************************** 2. row
***************************
  id: 1
  select_type:
SIMPLE
  table: B
  type: ref
  possible_keys: B_c2_ind
  key:
B_c2_ind
  key_len: 7
  ref:
example.A.c2
  rows: 2
  Extra: Using where;
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所得出的,如下例所示:




[email protected] :
example 02:46:15>
explain select A.* from A,B
where A.id
2 and A.c2
5 and A.c2 = B.c2 order by
B.c3\G

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

  id: 1
 select_type:
SIMPLE
  table: A
  type: range
  possible_keys:
PRIMARY
  key: PRIMARY
  key_len: 4
  ref:
NULL
 rows: 3

Extra: Using where; Using temporary; Using
filesort

  *************************** 2. row
***************************
  id: 1
  select_type:
SIMPLE
  table: B

  type: ref
  possible_keys: B_c2_ind
  key:
B_c2_ind
  key_len: 7
  ref:
example.A.c2
  rows: 2
  Extra: Using
where

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

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

时间: 2024-10-10 08:40:52

转载(MySQL Order By实现原理分析和Filesort优化)的相关文章

(转载)Java NIO:NIO原理分析(二)

NIO中的两个核心对象:缓冲区和通道,在谈到缓冲区时,我们说缓冲区对象本质上是一个数组,但它其实是一个特殊的数组,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况,如果我们使用get()方法从缓冲区获取数据或者使用put()方法把数据写入缓冲区,都会引起缓冲区状态的变化.本文为NIO使用及原理分析的第二篇,将会分析NIO中的Buffer对象. 在缓冲区中,最重要的属性有下面三个,它们一起合作完成对缓冲区内部状态的变化跟踪: position:指定了下一个将要被写入或者读取的元素索引,

【转载】Select函数实现原理分析

Select函数实现原理分析 <原文> select需要驱动程序的支持,驱动程序实现fops内的poll函数.select通过每个设备文件对应的poll函数提供的信息判断当前是否有资源可用(如可读或写),如果有的话则返回可用资源的文件描述符个数,没有的话则睡眠,等待有资源变为可用时再被唤醒继续执行. 下面我们分两个过程来分析select: 1. select的睡眠过程 支持阻塞操作的设备驱动通常会实现一组自身的等待队列如读/写等待队列用于支持上层(用户层)所需的BLOCK(阻塞)或NONBLO

Mysql报错注入原理分析(count()、rand()、group by)

0x00 疑问 一直在用mysql数据库报错注入方法,但为何会报错? 百度谷歌知乎了一番,发现大家都是把官网的结论发一下截图,然后执行sql语句证明一下结论,但是没有人去深入研究为什么rand不能和order by一起使用,也没彻底说明三者同时使用报错的原理. 0x01 位置问题? select count(*),(floor(rand(0)*2))x from information_schema.tables group by x; 这是网上最常见的语句,目前位置看到的网上sql注入教程,f

解读mysql主从配置及其原理分析(Master-Slave)

在windows下配置的,后面会在Linux下配置进行测试,需要配置mysql数据库同步的朋友可以参考下. 1.在主数据库服务器为从服务器添加一个拥有权限访问主库的用户:GRANT REPLICATION SLAVE ON *.* TO ' test'@'%' IDENTIFIED BY 'test'; (%表示允许所有IP,可设置指定从服务器IP)添加用户后:可在从服务器上用mysql -h127.0.0.1 -utest -ptest; 来测试是否有权限访问主数据库 2.在主据库配置文件加上

【MySQL 原理分析】之 Trace 分析 order by 的索引原理

一.背景 昨天早上,交流群有一位同学提出了一个问题.看下图: 我不是大佬,而且当时我自己的想法也只是猜测,所以并没有回复那位同学,只是接下来自己做了一个测试验证一下. 他只简单了说了一句话,就是同样的sql,一个没加 order by 就全表扫描,一个加了 order by 就走索引了. 我们可以仔细点看一下他提供的图(主要分析子查询即可,就是关于表 B 的查询,因为只有表 B 的查询前后不一致),我们可以先得出两个前提: 1.首先可以肯定的是,where 条件中的 mobile 字段是没有索引

MySQL数据库InnoDB存储引擎多版本控制(MVCC)实现原理分析

文/何登成 导读:   来自网易研究院的MySQL内核技术研究人何登成,把MySQL数据库InnoDB存储引擎的多版本控制(简称:MVCC)实现原理,做了深入的研究与详细的文字图表分析,方便大家理解InnoDB存储引擎实现的多版本控制技术(简称:MVCC). 基本知识 假设对于多版本控制(MVCC)的基础知识,有所了解.MySQL数据库InnoDB存储引擎为了实现多版本的一致性读,采用的是基于回滚段的协议. 行结构 MySQL数据库InnoDB存储引擎表数据的组织方式为主键聚簇索引.由于采用索引

MySQL主从复制的常见拓扑、原理分析以及如何提高主从复制的效率总结

一.主从复制搭建方法参考 1.MySQL5.6 数据库主从(Master/Slave)同步安装与配置详解请参考: http://blog.csdn.net/xlgen157387/article/details/51331244#comments 2.使用mysqlreplicate命令快速搭建 Mysql 主从复制: http://blog.csdn.net/xlgen157387/article/details/52452394 二.Mysql 主从复制的常用拓扑结构 2.1.一主一从 是最

(转)MySQL主从复制的常见拓扑、原理分析以及如何提高主从复制的效率总

二.Mysql 主从复制的常用拓扑结构 2.1.一主一从 是最基础的复制结构,用来分担之前单台数据库服务器的压力,可以进行读写分离.<喎?"/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMiBpZD0="22一主多从">2.2.一主多从 一台 Slave 承受不住读请求压力时,可以添加多台,进行负载均衡,分散读压力. 还可以对多台 Slave 进

MySQL Order By Rand()效率【转载】

最近由于需要大概研究了一下MYSQL的随机抽取实现方法.举个例子,要从tablename表中随机提取一条记录,大家一般的写法就是:SELECT * FROM tablename ORDER BY RAND() LIMIT 1. 但是,后来我查了一下MYSQL的官方手册,里面针对RAND()的提示大概意思就是,在ORDER BY从句里面不能使用RAND()函数,因为这样会导致数据列被多次扫描.但是在MYSQL 3.23版本中,仍然可以通过ORDER BY RAND()来实现随机. 但是真正测试一下