今天遇到一个filesort优化的案例,感觉不错,分享出来。

MySQL中filesort是什么意思?官方手册定义:

MySQL must do an extra pass to find out how to retrieve the rows in sorted order. The sort is done by going through     all rows according to the join type and storing the sort key and pointer to the row for all rows that match the WHERE clause    . The keys then are sorted and the rows are retrieved in sorted order。

一般来说如果有你的SQL查询语句中有order by且没有合适的索引时,通过EXPLAIN query可以在Extra列查看到Using filesort字样,当然一般来说此时也代表你需要去优化它了,无论是通过优化索引还是改变SQL查询实现方式。

先看表结构信息:

CREATE TABLE `tbxxxx` (
`id` int(10) unsigned NOT NULL auto_increment COMMENT '??id',
`a` varchar(20) default NULL COMMENT '??id',
`base62_id` varchar(10) default NULL COMMENT '??base62_id',
`userid` varchar(20) default NULL COMMENT '??id',
`category` int(5) unsigned default NULL COMMENT '????id',
`rate` decimal(10,2) NOT NULL default '0.00' COMMENT '??',
`status` enum('Y','N') NOT NULL default 'Y' COMMENT '??',
`releaseTime` datetime default NULL COMMENT '??????',
`createTime` datetime default NULL COMMENT '??????',
`content` text,
PRIMARY KEY (`id`),
UNIQUE KEY `a` (`a`),
KEY `releaseTime` (`releaseTime`),
KEY `rate` (`rate`),
KEY `crr` (`category`,`rate`,`releaseTime`),
KEY `idx_c_r_rate` (`category`,`releaseTime`,`rate`),
KEY `idx_status_rt_rate` (`status`,`releaseTime`,`rate`),
KEY `idx_status_rate_release` (`status`,`rate`,`releaseTime`)
) ENGINE=MyISAM AUTO_INCREMENT=3346255 DEFAULT CHARSET=utf8 COMMENT='?????'
1 row in set (0.15 sec)

ps:上面表结构中有些索引并不是线上实际存在的,这是做测试用临时添加的。

然后业务有如下查询:

SELECT a, content FROM tbxxxxx  WHERE `status`='Y' and releaseTime > '2013-07-08 11:00:00' ORDER BY rate DESC LIMIT 0, 10

对于这类型的查询可能第一反应是建立一个(status, releaseTime, rate)的复合索引, 然后通过EXPLAIN发现优化器也是这么选择的:

explain SELECT a, content FROM tbxxxxx  WHERE `status`='Y' and releaseTime > '2013-07-08 11:00:00' ORDER BY rate DESC LIMIT 0, 10;

+----+-------------+-----------+------+--------------------------------------------------------+--------------------+---------+-------+--------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+--------------------------------------------------------+--------------------+---------+-------+--------+-----------------------------+
| 1 | SIMPLE | tbxxxxx | ref | releaseTime,idx_status_rt_rate,idx_status_rate_release | idx_status_rt_rate | 1 | const | 531837 | Using where; Using filesort |
+----+-------------+-----------+------+--------------------------------------------------------+--------------------+---------+-------+--------+-----------------------------+
1 row in set (0.15 sec)

上面的key_len=1是能理解的,因为MySQL 5.6以前没有ICP,所releaseTime这种范围查询是无法利用索引。status占一个字节。但是从Extra中我们可以发现由于order by rate导致了filesort。那么这个索引该怎么调整才能避免filesort呢?其实在一刚开始没想到得一定去优化这个filesort的开销(后来证明这个案例中得filesort占了99%的开销),而是想着怎么去优化扫描的行数,使筛选的得到的行更少,这样可以减少回表带来的开销。但是通过打开profile之后,发现了问题最关键的地方:

上面的图标可以显示结果集排序占了绝大部分的时间开销。那么此时问题就变成了该怎么来优化掉这个可恶的filesort,于是添加了一个(status, rate, releaseTime)字段的索引,测试发现优化器依然默认会走(status, releaseTime, rate)这个复合索引, 于是只能通过用use index()的语法来强制走目标索引:

explain SELECT a, content FROM tbxxxxx use index(idx_status_rate_release) WHERE `status`='Y' and releaseTime > '2013-07-08 11:00:00' ORDER BY rate DESC LIMIT 0, 10;
+----+-------------+-----------+------+-------------------------+-------------------------+---------+-------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+-------------------------+-------------------------+---------+-------+---------+-------------+
| 1 | SIMPLE | tbxxxxxx | ref | idx_status_rate_release | idx_status_rate_release | 1 | const | 1057094 | Using where |
+----+-------------+-----------+------+-------------------------+-------------------------+---------+-------+---------+-------------+
1 row in set (0.00 sec)

从执行计划可以看出, 此时没有了filesort这个阶段,因为默认status, rate这个索引前缀就是按照rate来排序的,因此正好可以利用索引的数据有序来达到最终的order by rate效果。细心的朋友可能会发现在这个执行计划中估算扫描的行数是上一个执行计划的2倍, 多扫描这么多行开销不大吗(这也是为什么当初第一反应优化这条SQL时会考虑优化索引来减少扫描的行数)? 事实上证明这个开销相对于filesort来说已经很小,有两点可以证明:profile的分析显示99%的时间是耗在sorting result,第二,在线上一个从库上测试第二个执行计划查询时间为0.1s左右,而第一个执行计划的查询时间在3s左右。虽然说一般来说并不建议使用use index这种语法,后期的不确定性较大,但是既然提供这种语法就有它自己的理由, 用好了自然有优势,先这么将就着吧。另外这里需要说明的是:(status, rate, releaseTime)这个索引中只有rate是必须得, status由于只有两个取值,筛选效果很不明显, releaseTime用不到(ICP后可能能用到)

最后是两点小感悟:1.MySQL的优化器还是那么有点坑爹, 有时候不靠谱。2.有时候优化一个SQL还是不能完全凭直觉,平常的经验,profile这种命令可以完全将一个SQL查询执行过程中各个阶段的开销都统计出来,这样我们就有了针对一个SQL的优化关键点,这样才能做不到不盲目,从而高效快速的优化。

MySQL filesort优化案例一则的更多相关文章

  1. MySQL参数优化案例

    环境介绍 优化层级与指导思想 优化过程 最小化安装情况下的性能表现 优化innodb_buffer_pool_size 优化innodb_log_files_in_group&innodb_l ...

  2. MySQL索引优化案例

    这里我们分成三种情况进行分析,分别是单表,两表,三表 1.单表 CREATE TABLE IF NOT EXISTS `article`( `id` ) NOT NULL PRIMARY KEY AU ...

  3. MySQL索引优化案例浅析

    MySQL是关系型数据库的一种,查询功能强,数据一致性高,数据安全性高,支持二级索引.但是性能比起非关系型数据库稍弱,特别是百万级以上的数据,很容易出现查询慢的现象.这时候要分析慢的原因,一般情况下是 ...

  4. mysql depended_query 优化案例一则

    月度利息统计sql优化 原因:写的sql语句复杂,理解起来有难度,另一方面,查询性能比较低 原来的语句如下: SELECT tp.year, tp.month, tp.bid_id, b.`title ...

  5. MySQL的索引单表优化案例分析

    建表 建立本次优化案例中所需的数据库及数据表 CREATE DATABASE db0206; USE db0206; CREATE TABLE `db0206`.`article`( `id` INT ...

  6. mysql优化案例

    MySQL优化案例 Mysql5.1大表分区效率测试 Mysql5.1大表分区效率测试MySQL | add at 2009-03-27 12:29:31 by PConline | view:60, ...

  7. 记一次mysql多表查询(left jion)优化案例

    一次mysql多表查询(left jion)优化案例 在新上线的供需模块中,发现某一个查询按钮点击后,出不来结果,找到该按钮对应sql手动执行,发现需要20-30秒才能出结果,所以服务端程序判断超时, ...

  8. Mysql 排序优化与索引使用(转)

    为了优化SQL语句的排序性能,最好的情况是避免排序,合理利用索引是一个不错的方法.因为索引本身也是有序的,如果在需要排序的字段上面建立了合适的索引,那么就可以跳过排序的过程,提高SQL的查询速度.下面 ...

  9. MySQL分页优化中的“INNER JOIN方式优化分页算法”到底在什么情况下会生效?

    本文出处:http://www.cnblogs.com/wy123/p/7003157.html 最近无意间看到一个MySQL分页优化的测试案例,并没有非常具体地说明测试场景的情况下,给出了一种经典的 ...

随机推荐

  1. ViewPager,使用Fragment实现

    效果如图: 使用Fragment实现tab的缺点就是不能够滑动.不过应该也算优点,具体场景可以自由选择. 完整代码:imooc-tab022fragment,在我的百度云网盘上. MainAcgtiv ...

  2. Memcache 查看列出所有key方法

    参考博文: Memcache 查看列出所有key方法 1. cmd上登录memcache telnet 127.0.0.1 11211  2. 列出所有keys stats items // 这条是命 ...

  3. HDOJ 3635 并查集- 路径压缩,带秩合并

    思路来源:http://blog.csdn.net/niushuai666/article/details/6990421 题目大意: 初始时,有n个龙珠,编号从1到n,分别对应的放在编号从1到n的城 ...

  4. QT全平台设置图标,全平台静态编译 good

    1.  概述 当我们用QT写好了一个软件,要把你的程序分享出去的时候,不可能把编译的目录拷贝给别人去运行.编译好的程序应该是一个主程序,加一些资源文件,再加一些动态链接库,高大上一些的还可以做一个安装 ...

  5. 使用jstl 截取字符串

    时常碰见这样的 问题:获取数据库中的文本域的时候经常是在p标签中的,在页面显示的时候也是带着p标签,如何去除p标签呢 这里提供一个使用jstl的方式 1.首先导入jstl的函数标签库 <%@ t ...

  6. java入门(1) 程序运行机制及运行过程

    首先我们来看一下java程序在底层是怎么工作的: JAVA有两种核心机制: Java虚拟机(Java Virtual Machine): 1.java虚拟机可以理解成一个以字节码为机器指令的CPU. ...

  7. linux ubuntu安装jdk

    Oracle对Jdk7与Jre7的关系的经典图解 Oracle has two products that implement Java Platform Standard Edition(Java ...

  8. 设计模式 ( 十五 ) 中介者模式Mediator(对象行为型)

    设计模式 ( 十五 ) 中介者模式Mediator(对象行为型) 1.概述 在面向对象的软件设计与开发过程中,根据“单一职责原则”,我们应该尽量将对象细化,使其只负责或呈现单一的职责,即将行为分布到各 ...

  9. 基于visual Studio2013解决C语言竞赛题之0522和为素

     题目

  10. Thinkphp里import的几个使用方法介绍

    以下附上import的几个使用方法介绍 1.使用方法一 import('@.Test.Translate'); @,表示项目根文件夹.假定根文件夹是:App/ 导入类库的路径是:App/Lib/Tes ...