order by 排序优化

写在前面

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

MySQL 排序方式

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

  • 经过对返回数据进行排序,即 FileSort 排序。算法

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

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 使用联合索引进行排序ui

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 讲》

最后

若是你们想要实时关注我更新的文章以及我分享的干货的话,能够关注个人公众号 咱们都是小白鼠

感谢您的关注,若是您喜欢的话,能够点击右下方的在看,也欢迎您把这篇文章分享给更多的朋友,谢谢!


天天进步一点点!!!

于 2020.05.28

相关文章
相关标签/搜索