写在前面

文章涉及到的 customer 表来源于案例库 sakila,下载地址为 http://downloads.mysql.com/docs/sakila-db.zip

MySQL 排序方式

  • 通过索引顺序扫描直接返回有序数据

  • 通过对返回数据进行排序,即 FileSort 排序。

所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。FileSort 并不代表通过磁盘文件进行排序,而只是说进行了一个排序操作,至于排序操作是否使用了磁盘文件或临时表取决于 MySQL 服务器对排序参数的设置和需要排序数据的大小。

EXPLAIN 排序分析

customer DDL

CREATE TABLE `customer` (
`customer_id` smallint unsigned NOT NULL AUTO_INCREMENT,
`store_id` tinyint unsigned NOT NULL,
`first_name` varchar(45) NOT NULL,
`last_name` varchar(45) NOT NULL,
`email` varchar(50) DEFAULT NULL,
`address_id` smallint unsigned NOT NULL,
`active` tinyint(1) NOT NULL DEFAULT '1',
`create_date` datetime NOT NULL,
`last_update` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`customer_id`),
KEY `idx_fk_store_id` (`store_id`),
KEY `idx_fk_address_id` (`address_id`),
KEY `idx_last_name` (`last_name`),
KEY `idx_storeid_email` (`store_id`,`email`),
CONSTRAINT `fk_customer_address` FOREIGN KEY (`address_id`) REFERENCES `address` (`address_id`) ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT `fk_customer_store` FOREIGN KEY (`store_id`) REFERENCES `store` (`store_id`) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=600 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; CREATE DEFINER=`root`@`%` TRIGGER `customer_create_date` BEFORE INSERT ON `customer` FOR EACH ROW SET NEW.create_date = NOW();

返回所有用户记录,根据 customer_id 进行排序。因为 customer_id 是主键,记录都是按照主键排好序的,所以无需进行额外的排序操作,直接返回所有数据即可。

EXPLAIN SELECT * FROM customer ORDER BY customer_id;


由于默认是根据 customer_id 进行排序的,相对于上面来说,这里需要全表扫描,然后根据 store_id 对全表扫描结果进行排序,因此这里用到了 FileSort 排序。

EXPLAIN SELECT * FROM customer ORDER BY store_id;


store_id , email 这两个字段是有联合索引 idx_storeid_email 的,只查询 store_id 和 email 两个字段,直接通过联合索引所在的 B+ 树返回查询数据(该索引树先根据 store_id 字段先进行排序,然后再根据 email 字段排序好的),所以这里的查询结果就是排序好的。

EXPLAIN SELECT store_id , email FROM customer ORDER BY store_id;


和上面不同的是,排序的字段是 email,导致 FileSort 排序的原因是联合索引 idx_storeid_email 的是先根据 store_id 字段先进行排序,然后再根据 email 字段排序好的,如果直接使用 email 排序,则无法使用 idx_storeid_email 索引树排好的顺序。

EXPLAIN SELECT store_id , email FROM customer ORDER BY email;


ORDER BY 使用联合索引进行排序

EXPLAIN SELECT store_id , email FROM customer ORDER BY store_id , email;


ORDER BY 的字段混用 ASC 和 DESC 会导致使用 FileSort 排序。

EXPLAIN SELECT store_id , email FROM customer ORDER BY store_id ASC , email DESC;


固定 store_id = 1 情况下对 email 字段进行排序,使用 idx_storeid_email 索引即可

EXPLAIN SELECT store_id , email FROM customer WHERE store_id  = 1 ORDER BY email;


where 条件先进行 store_id 范围查询导致 ORDER BY email 字段无法使用 idx_storeid_email 索引进行排序。

EXPLAIN SELECT store_id , email FROM customer WHERE store_id  >= 1 AND store_id <= 3 ORDER BY email;


ORDER BY 可能出现 FileSort 的几种情况:

  1. order by 字段混用 ASC 和 DESC 排序方式。
  2. SELECT * 时,如果 order by 排序字段不是主键可能导致 FileSort。
  3. 联合索引情况下,order by 多字段排序的字段左右顺序和联合索引的字段左右顺序不一致导致 FileSort。
  4. 联合索引情况下,where 字段和 order by 字段的左右顺序和联合索引字段左右顺序或者 where 字段出现范围查询都可能导致 FileSort。

FileSort 优化

通过创建合适的索引可以减少 FileSort 的出现,但是对于某些情况下,条件限制不能让 FileSort 彻底消失,那就需要对 FileSort 进行优化。对于 FileSort,MySQL 有两种排序算法。

  • 两次扫描算法(Two Passes):首先根据条件取出排序字段和行指针,之后在排序区 sort buffer 中排序。如果排序区 sort buffer 不够,则在临时表 Temporary Table 中存储排序结果。完成排序后根据行指针回表读取完整记录。该算法是 MySQL 4.1 之前采用的算法,需要两次访问数据,第一次获取排序字段和行指针信息,第二次根据行指针获取完整记录,尤其是第二次读取操作可能导致大量随机 IO 操作;有点是排序的时候内存开销比较小。

  • 一次扫描算法(Single Passes):一次性取出满足条件的行的所有字段,然后在排序区 sort buffer 中排序后直接输出结果集。排序的时候内存开销比较大,但是排序效率比两次扫描算法要高。

MySQL 通过比较系统变量 max_length_for_sort_data 的大小和 Query 语句取出的字段总大小来判断使用哪种排序算法。如果 max_length_for_sort_data 设置足够大,那么会使用一次扫描算法;否则使用两次扫描算法。适当加大系统变量 max_length_for_sort_data 的值,能够让 MySQL 选择更加优化的 FileSort 排序算法。当然,假如 max_length_for_sort_data 设置过大,会造成 CPU 利用率过低和磁盘 IO 过高。

适当加大 sort_buffer_size 排序区,尽量让排序在内存中完成,而不是通过创建临时表放在文件中进行;当然也不能无限加大 sort_buffer_size 排序区,因为 sort_buffer_size 参数是每个线程独占的,设置过大会导致服务器 SWAP 严重,要考虑数据库活动连接数和服务器内存的大小来适当设置排序区。

尽量只使用必要的字段,SELECT 具体的字段名称,而不是 SELECT * 选择所有字段,这样可以减少排序区的使用,提高 SQL 性能。

参考

《深入浅出 MySQL 数据库开发、优化与管理维护第 2 版》

《MySQL 实战 45 讲》

最后

如果大家想要实时关注我更新的文章以及我分享的干货的话,可以关注我的公众号 我们都是小白鼠

Order by 优化的更多相关文章

  1. 8.2.1.15 ORDER BY Optimization ORDER BY 优化

    8.2.1.15 ORDER BY Optimization ORDER BY 优化 在一些情况下, MySQL 可以使用一个索引来满足一个ORDER BY 子句不需要做额外的排序 index 可以用 ...

  2. Mysql序列(七)—— order by优化

    前言 在mysql中满足order by的处理方式有两种: 让索引满足排序,即扫描有序索引然后再找到对应的行结果,这样结果即是有序: 使用索引查询出结果或者扫描表得到结果然后使用filesort排序: ...

  3. MySQL性能优化,MySQL索引优化,order by优化,explain优化

    前言 今天我们来讲讲如何优化MySQL的性能,主要从索引方面优化.下期文章讲讲MySQL慢查询日志,我们是依据慢查询日志来判断哪条SQL语句有问题,然后在进行优化,敬请期待MySQL慢查询日志篇 建表 ...

  4. Mysql查询优化汇总 order by优化例子,group by优化例子,limit优化例子,优化建议

    Mysql查询优化汇总 order by优化例子,group by优化例子,limit优化例子,优化建议 索引 索引是一种存储引擎快速查询记录的一种数据结构. 注意 MYSQL一次查询只能使用一个索引 ...

  5. mysql order by 优化 |order by 索引的应用

    在某些场景,在不做额外的排序情况下,MySQL 可以使用索引来满足 ORDER BY 子句的优化.虽然 ORDER BY并不完全精确地匹配索引,但是索引还是会被使用,只要在WHERE子句中,所有未被使 ...

  6. order by优化--Order By实现原理分析和Filesort优化

    在MySQL中的ORDER BY有两种排序实现方式: 1.利用有序索引获取有序数据 2.文件排序 在使用explain分析查询的时候,利用有序索引获取有序数据显示Using index.而文件排序显示 ...

  7. MySQL高级知识(八)——ORDER BY优化

    前言:在使用order by时,经常出现Using filesort,因此对于此类sql语句需尽力优化,使其尽量使用Using index. 0.准备 #1.创建test表. drop table i ...

  8. mysql实战优化之六:Order by优化 sql优化、索引优化

    在MySQL中的ORDER BY有两种排序实现方式: 1.利用有序索引获取有序数据 2.文件排序 在使用explain分析查询的时候,利用有序索引获取有序数据显示Using index.而文件排序显示 ...

  9. 八、ORDER BY优化

    前言:在使用order by时,经常出现Using filesort,因此对于此类sql语句需尽力优化,使其尽量使用Using index. 0.准备 #1.创建test表. drop table i ...

随机推荐

  1. Nginx模块开发(4)————使用subrequest访问第三方服务

    该模块可以完成如下的功能,当我们输入http://你的ip/lcw?s_sh000001时,会使用subrequest方式得到新浪服务器上的上证指数,代码如下: //start from the ve ...

  2. linux 服务器/客户端 tcp通信的简单例子

    昨天弄了sublime之后没有弄输入中文的了,学生党来着,刚好可以练练英语(英语渣渣,还要考六级),所以注释都写英文的(语法什么的就别太深究了) 服务器端: /*start from the very ...

  3. 题目分享F 二代目

    题意:T个点R种双向边,P种单向边,求点S到每个点的最短距离 分析:(这再看不出来是spfa就该**了) 首先,这题能否用spfa就看他是否有负环呗,显然,双向边的权值非负,单向边还有个啥政策,总之显 ...

  4. 细说 PEP 468: Preserving Keyword Argument Order

    细说 PEP 468: Preserving Keyword Argument Order Python 3.6.0 版本对字典做了优化,新的字典速度更快,占用内存更少,非常神奇.从网上找了资料来看, ...

  5. MODIS系列之NDVI(MOD13Q1)五:NDVI处理流程

    前言:(个人建议) 1.进行数据处理工作,由于通常数据量比较大.所以在个人电脑中,要将基础数据.不断增长的过程数据.结果数据等分门别类,使得简单易找. 2.将重要数据备份,因为在数据处理过程中,由于出 ...

  6. 【大数据 Spark】利用电影观看记录数据,进行电影推荐

    利用电影观看记录数据,进行电影推荐. 目录 利用电影观看记录数据,进行电影推荐. 准备 1.任务描述: 2.数据下载 3.部分数据展示 实操 1.设置输入输出路径 2.配置spark 3.读取Rati ...

  7. 【HBase】表模型和基本操作介绍

    目录 HBase表模型 常用Shell操作 HBase表模型 创建一个hbase表,必须要有表名和列族名 列族 -- columnFamily,就是上图中的Column Family1 userInf ...

  8. python是如何进行参数传递的?

    在分析python的参数传递是如何进行的之前,我们需要先来了解一下,python变量和赋值的基本原理,这样有助于我们更好的理解参数传递. python变量以及赋值 数值 从几行代码开始 In [1]: ...

  9. Python 简明教程 --- 1,搭建Python 环境

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 人生苦短,我用Python. -- 龟叔T恤 Python 是一门解释型语言,所以要想运行Pytho ...

  10. java ->Servlet接口

    JavaWeb核心之Servlet Servlet简介 什么是Servlet(控制器的作用) Servlet 运行在服务端的Java小程序,是sun公司提供一套规范(接口),用来处理客户端请求.响应给 ...