MySQL中如何优化order by语句

order by 查询语句使用也是非常频繁,有时候数据量大了会发现排序查询很慢,本文就介绍一下 MySQL 是如何进行排序的,以及如何利用其原理来优化 order by 语句。

建立一张表:

CREATE TABLE `cc4` (
  `id` INT(11) NOT NULL,
  `user_name` VARCHAR(16) NOT NULL,
  `job` VARCHAR(16) NOT NULL,
  `company` VARCHAR(16) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `company_index` (`company`)
) ENGINE=INNODB;

建完表之后,再创建一个脚本,在脚本中插入 2000 条数据到前面建好的表cc4 中:

DROP PROCEDURE IF EXISTS cc4_data;
DELIMITER ;;
CREATE PROCEDURE cc4_data()
BEGIN
  DECLARE i INT;
  DECLARE company VARCHAR(128);
  SET i=1;
  WHILE(i<=2000) DO
    IF i%6 = 0
      THEN SET company= '证券';
    ELSEIF i%6 = 1
      THEN SET company= '银行';
    ELSEIF i%6 = 2
      THEN SET company= '保险';
    ELSEIF i%6 = 3
      THEN SET company= '科技';
    ELSEIF i%6 = 4
      THEN SET company= '金融';
    ELSE
      SET company ='传统';
    END IF;
    INSERT INTO cc4 VALUES(i, CONCAT('孤狼',i), CONCAT('程序员',i),company);
    SET i=i+1;
  END WHILE;
END;;
DELIMITER ;
CALL cc4_data();

这时候我们如果想要对某一家公司里面的人按照名字进行排序,一般会这么写:

SELECT user_name,job,company FROM cc4 WHERE company='科技' ORDER BY user_name LIMIT 1000;

这是一条非常简单且常见的 sql 语句,但是就是这么简单的一条 sql,它到底是如何被执行的呢?

全字段排序法

首先我们对上面的语句执行 explain 语句,看看是怎么执行的:

explain SELECT user_name,job,company FROM cc4 WHERE company='科技' ORDER BY user_name LIMIT 1000;

可以看到,在最后一列 Extra 中显示 Using filesort,也就是说用到了文件排序,这个文件排序是如何执行的呢?

大概画出如下一个草图表示表 cc4 中的索引示意图:

上图中显示 company 字段为普通索引,再加上主键索引,这张表一共有两个索引,所以这条语句是这么执行的:

  • 初始化 sort_buffer,并确定好需要放入 user_name ,job,company 这三个字段。
  • 从 company 索引中找到第一个满足 company='科技’ 条件的主键 id,也就是上图中的 ID-3。
  • 然后执行回表操作,根据 id 值到主键索引中取出整行,然后取出 user_name ,job,company 三个字段的值,并存入sort_buffer 中。
  • 从 company 索引中取下一个满足条件记录的主键 id,重复步骤 3 。
  • 继续重复 步骤 4 和 3,直到 company 的值不满足查询条件为止。
  • 对 sort_buffer 中的数据按照字段 user_name 做快速排序,最后按照排序结果取前 1000 行返回给客户端。

这种排序方式称之为全字段排序法。

上面步骤中的第 6 步,排序可以在内存中进行,如果内存足够的话,而内存是否足够则取决于 sort_buffer_size 的值,但是我们想一下,如果排序的数据量太大,我们不可能提供足够的内存,那么这时候就不得不使用磁盘的临时文件来进行排序。

那么我们如何知道当前的排序语句是使用文件完成排序还是使用内存来完成排序呢?

接下来我们执行下面两句话:

SET optimizer_trace='enabled=on';-- 打开optimizer_trace,只对本线程有效
SELECT user_name,job,company FROM cc4 WHERE company='科技' ORDER BY user_name LIMIT 1000;
SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`\G -- 查看 OPTIMIZER_TRACE 输出

最后这条查询语句会返回非常多的信息,包括了具体的查询步骤,我们看到最后的 filesort_summary:

这里面有几个信息比较关键:

  • memory_available:表示当前可以用于排序的内存
  • num_rows_found:表示有多少条数据参与排序。
  • num_initial_chunks_spilled_to_disk:表示产生了多少个临时文件用于排序,0表示当前是全部采用内存排序,这里为什么会产生多个文件的原因是当数据量过大时,MySQL会分散到多个文件进行处理,最后通过归并排序算法来完成完整的排序。
  • sort_mode:最后这一列代表当前排序模式,packed_additional_fields代表的就是采用了全字段排序法,而且启用了 pack。

接下来我们把默认的排序内存改小一点:

SET sort_buffer_size=32768; -- 8.0 版本最小值,无法设置成更小,不同版本之间有差异
show variables like 'sort_buffer_size';

执行之后可以看到排序大小已经被修改为 32k:

接下来我们再来执行排序查询跟踪

SELECT user_name,job,company FROM cc4 WHERE company='科技' ORDER BY user_name LIMIT 1000;
SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`\G -- 查看 OPTIMIZER_TRACE 输出

这时候会发现这时候使用到了 6 个临时文件进行排序:

主键排序法

在前面的全字段排序法中其实有些浪费,因为排序只用到了 user_name 字段,而我们却同时查询了其他字段,这些字段查询出来都是会占用空间的,尤其是当查询的字段很多,或者有些字段又特别长的时候,会占用很大空间,导致不得不使用文件排序,而由于字段多又长,就会造成文件个数增多,从而导致排序性能会更差。

上面的查询语句中,我们有没有办法不把一些无用的字段也放到 sort_buffer 中呢?

在 MySQL 中提供了一个字段 max_length_for_sort_data,默认是 4096

show variables like 'max_length_for_sort_data';

这个字段是控制用于排序的行数据的长度的一个参数。如果用于排序的单行数据长度超过这个值,MySQL 就认为单行数据太大了,要换一个算法,采用 rowid 算法。

采用 rowid 算法的步骤如下:

  • 初始化 sort_buffer,并确定好需要放入 user_name ,id 这两个字段。
  • 从 company 索引中找到第一个满足 company='科技’ 条件的主键 id,也就是上图中的 ID-3。
  • 然后执行回表操作,根据 id 值到主键索引中查找出整行数据,然后取出 user_name ,id 这两个字段的值,并存入sort_buffer 中。
  • 从 company 索引中取下一个满足条件记录的主键 id,重复步骤 3 。
  • 继续重复 步骤 4 和 3,直到 company 的值不满足查询条件为止。
  • 对 sort_buffer 中的数据按照字段 user_name 做快速排序。
  • 遍历排序结果,取前 1000 行数据,并根据主键 id 进行回表查询,取出 user_name,job 和 company三个字段返回给客户端。

这种排序方式对比前面一种全字段排序,我们发现存的数据更少了,所以需要的内存空间更少,但是又有一个更大的问题就是这里需要进行两次回表操作,当数据量过大,这也会造成性能影响。

所以我们再结合前面学习的知识,如果排序的时候可以采用覆盖索引,那么就不需要进行回表操作,从而大幅度提升性能,这也是覆盖索引的威力。

如何避免 filesort

首先我们看下面一个例子,执行以下语句:

DROP INDEX company_index ON cc4;-- 删除索引
CREATE INDEX company_user_index ON cc4 (company,user_name);-- 创建联合索引
explain SELECT user_name,job,company FROM cc4 WHERE company='科技' ORDER BY user_name LIMIT 1000;

执行结果如下:

可以看到,这次就没有用到 filesort 了,这是为什么呢?

因为我们创建了一个联合索引,而 MySQL 中的 B+ 树索引是天然有序的,所以当指定了 company,按顺序找到的数据,就是按照 user_name 进行的排序,也就不需要再执行一次排序操作了。

到此这篇关于MySQL中如何优化order by语句的文章就介绍到这了,更多相关MySQL优化order by内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MySQL group by和order by如何一起使用

    假设有一个表:reward(奖励表),表结构如下: CREATE TABLE test.reward ( id int(11) NOT NULL AUTO_INCREMENT, uid int(11) NOT NULL COMMENT '用户uid', money decimal(10, 2) NOT NULL COMMENT '奖励金额', datatime datetime NOT NULL COMMENT '时间', PRIMARY KEY (id) ) ENGINE = INNODB A

  • MySQL Order By语法介绍

    今天在使用ORDER BY的过程中出现了一点问题,发现之前对ORDER BY理解是错误的. 之前在w3s网站上看到ORDER BY的用法,以为是对选出来的数据按关键字升序或者降序排列,结果今天尝试select数据集数据的时候,发现使用ORDER BY 和ORDER BY DESC得出的查询结果完全不一样,按照自己之前的理解它们应该是结果相同,而内部顺序不一样而已. 问了一下同事,查了一下文档,才恍然大悟.如果我们在执行select语句的时候使用ORDER BY (DESC),那么它首先会对所有记

  • 深入解析mysql中order by与group by的顺序问题

    mysql 中order by 与group by的顺序是:selectfromwheregroup byorder by注意:group by 比order by先执行,order by不会对group by 内部进行排序,如果group by后只有一条记录,那么order by 将无效.要查出group by中最大的或最小的某一字段使用 max或min函数.例:select sum(click_num) as totalnum,max(update_time) as update_time,

  • mysql中count(), group by, order by使用详解

    最近做IM的时候遇到一个问题,同时用到了这三个关键字.就是查询一个人的离线消息详情,我们服务端返回给客户端显示的这个详情包括了三个内容,第一个要求列出离线这段时间哪些人或者群给你发了消息,第二个这其中的某个人或者群发了多少条离线消息,第三个拿出最新的一条显示出来.很明显,group by分组哪些人或者群给你发了离线消息,count()得到离线消息数量,order by时间来排序拿出最新的消息. select count(1) as cnt, msg_data from t_im_chat_off

  • MySQL order by性能优化方法实例

    前言 工作过程中,各种业务需求在访问数据库的时候要求有order by排序.有时候不必要的或者不合理的排序操作很可能导致数据库系统崩溃.如何处理好order by排序呢?本文从原理以及优化层面介绍 order by . 一 MySQL中order by的原理 1 利用索引的有序性获取有序数据 当查询语句的 order BY 条件和查询的执行计划中所利用的 Index 的索引键(或前面几个索引键)完全一致,且索引访问方式为 rang,ref 或者 index 的时候,MySQL 可以利用索引顺序而

  • Mysql中order by、group by、having的区别深入分析

    order by 从英文里理解就是行的排序方式,默认的为升序. order by 后面必须列出排序的字段名,可以是多个字段名.  group by 从英文里理解就是分组.必须有"聚合函数"来配合才能使用,使用时至少需要一个分组标志字段. 什么是"聚合函数"? 像sum().count().avg()等都是"聚合函数" 使用group by 的目的就是要将数据分类汇总. 一般如: select 单位名称,count(职工id),sum(职工工资)

  • MySQL Order by 语句用法与优化详解

    MySQL Order By keyword是用来给记录中的数据进行分类的.MySQL Order By Keyword根据关键词分类ORDER BY keyword是用来给记录中的数据进行分类的. 复制代码 代码如下: SELECT column_name(s) FROM table_name ORDER BY column_name 例子 SQL创建代码: 复制代码 代码如下: CREATE TABLE IF NOT EXISTS mysql_order_by_test (  uid int

  • MySQL Order By索引优化方法

    尽管 ORDER BY 不是和索引的顺序准确匹配,索引还是可以被用到,只要不用的索引部分和所有的额外的 ORDER BY 字段在 WHERE 子句中都被包括了. 使用索引的MySQL Order By 下列的几个查询都会使用索引来解决 ORDER BY 或 GROUP BY 部分: 复制代码 代码如下: SELECT * FROM t1 ORDER BY key_part1,key_part2,... ; SELECT * FROM t1 WHERE key_part1=constant ORD

  • MySQL中如何优化order by语句

    order by 查询语句使用也是非常频繁,有时候数据量大了会发现排序查询很慢,本文就介绍一下 MySQL 是如何进行排序的,以及如何利用其原理来优化 order by 语句. 建立一张表: CREATE TABLE `cc4` ( `id` INT(11) NOT NULL, `user_name` VARCHAR(16) NOT NULL, `job` VARCHAR(16) NOT NULL, `company` VARCHAR(16) DEFAULT NULL, PRIMARY KEY

  • Mysql优化order by语句的方法详解

    本篇文章我们将了解ORDER BY语句的优化,在此之前,你需要对索引有基本的了解,不了解的老少爷们可以先看一下我之前写过的索引相关文章.现在让我们开始吧. MySQL中的两种排序方式 1.通过有序索引顺序扫描直接返回有序数据 因为索引的结构是B+树,索引中的数据是按照一定顺序进行排列的,所以在排序查询中如果能利用索引,就能避免额外的排序操作.EXPLAIN分析查询时,Extra显示为Using index. 2.Filesort排序,对返回的数据进行排序 所有不是通过索引直接返回排序结果的操作都

  • MySQL利用索引优化ORDER BY排序语句的方法

    创建表&创建索引 create table tbl1 ( id int unique, sname varchar(50), index tbl1_index_sname(sname desc) ); 在已有的表创建索引语法 create [unique|fulltext|spatial] index 索引名 on 表名(字段名 [长度] [asc|desc]); MySQL也能利用索引来快速地执行ORDER BY和GROUP BY语句的排序和分组操作. 通过索引优化来实现MySQL的ORDER

  • MySQL中数据库优化的常见sql语句总结

    目录 1.SHOW ENGINES 2.SHOW PROCESSLIST 3.SHOW STATUS LIKE 'InnoDB_row_lock%' 4.SHOW ENGINE INNODB STATUS 5.SHOW INDEXS 6.ALTER TABLE xx ENGINE = INNODB 7.ANALYZE TABLE 1.SHOW ENGINES 查看执行引擎以及默认引擎. 2.SHOW PROCESSLIST SHOW PROCESSLIST查看当前数据库连接的使用情况,以及各种状

  • MySQL中union和order by同时使用的实现方法

    MySQL中union和order by是可以一起使用的,但是在使用中需要注意一些小问题,下面通过例子来说明.首先看下面的t1表. 1.如果直接用如下sql语句是会报错:Incorrect usage of UNION and ORDER BY. SELECT * FROM t1 WHERE username LIKE 'l%' ORDER BY score ASC UNION SELECT * FROM t1 WHERE username LIKE '%m%' ORDER BY score A

  • 分析mysql中一条SQL查询语句是如何执行的

    目录 一.MySQL 逻辑架构概览 二.连接器(Connector) 三.查询缓存(Query Cache) 四.解析器(Parser) 五.优化器(Optimizer) 六.执行器 七.小结 一.MySQL 逻辑架构概览 MySQL 最重要.最与众不同的特性就是它的可插拔存储引擎架构(pluggable storage engine architecture),这种架构的设计将查询处理及其他系统任务和数据的存储/提取分离开来.来看官网的解释: The MySQL pluggable stora

  • MySQL中一条SQL查询语句是如何执行的

    目录 前言 1. 处理连接 1.1 客户端和服务端的通信方式 1.1.1 TCP/IP协议 1.1.2 UNIX域套接字 1.1.3 命名管道和共享内存 1.2 权限验证 1.3 查看MySQL连接 2. 解析与优化 2.1 查询缓存 2.2 解析器 & 预处理器(Parser & Preprocessor) 2.2.1 词法解析 2.2.2 语法分析 2.2.3 预处理器 2.3 查询优化器(Optimizer)与查询执行计划 2.3.1 什么是查询优化器? 2.3.2 优化器究竟做了什

  • 深入了解MySQL中索引优化器的工作原理

    目录 本文导读 一.MySQL 优化器是如何选择索引的 1.MySQL数据库组成 2.MySQL数据库成本计算 二.MySQL查询成本 三.SELECT 执行过程 总结 本文导读 本文将解读MySQL数据库查询优化器(CBO)的工作原理.简单介绍了MySQL Server的组成,MySQL优化器选择索引额原理以及SQL成本分析,最后通过 select 查询总结整个查询过程. 一.MySQL 优化器是如何选择索引的 下面我们来看这张表,SUB_ODR_ID字段创建了相关的 2 个索引,根据我们前面

  • MYSQL中binlog优化的一些思考汇总

    问题 问题1:如何解决事务提交时flush redo log带来的性能损失 WAL是实现事务持久性(D)的一个常用技术,基本原理是将事务的修改记录redo log.redo log顺序追加写入.事务提交时,只需要保证事务的redo log落盘即可,通过redo log的顺序写代替页面的随机写提升数据库系统的性能.但是,该方案必须要求每个事务提交时都将其生成的redo log进行一次刷盘,效率不高. 问题2:binlog和引擎层事务提交的顺序问题 对于单个事务而言,日志写入顺序是先redo log

  • Mysql中通用表达式WITH AS语句的使用实例代码

    目录 一.WITH AS 的含义 二.WITH AS 的含使用 三.WITH AS 的作用 补充:使用with as应该注意 总结 一.WITH AS 的含义 使用with as 可以让子查询重用相同的with查询块,并在select查询块中直接引用,一般用在select查询块会多次使用某个查询sql时,会把这个sql语句放在with as 中,作为公用的表达式,通过别名的方式在主查询语句中重复使用. 二.WITH AS 的含使用 现有两张表cust_info_detail(客户信息表,表中三个

随机推荐