今天遇到一个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的优化关键点,这样才能做不到不盲目,从而高效快速的优化。

转载于:https://www.cnblogs.com/pangblog/p/3313203.html

MySQL filesort优化案例一则相关推荐

  1. mysql 经典优化案例_MySQL-SQL优化10大最经典案例详解

    SQL语句 select * from _t where a = 1 and b = 2 order by c desc limit 10000, 10; 对于大分页的场景,可以优先让产品优化需求,如 ...

  2. MySQL - 索引优化案例实操

    文章目录 生猛干货 DB Version Table Case 1 : 联合索引第一个字段用范围不一定会走索引 优化一 强制走索引 force index(idx_name_age_position) ...

  3. mysql depended_query 优化案例一则

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

  4. 75-100-024-测试-MySQL 双表优化案例

    文章目录 1.建表 2.左连接 3.小结 4. 大结 1.建表 CREATE TABLE IF NOT EXISTS class(id INT(10) UNSIGNED NOT

  5. mysql索引优化实际例子_MySQL索引优化的实际案例分析

    Order by desc/asc limit M是我在mysql sql优化中经常遇到的一种场景,其优化原理也非常的简单,就是利用索引的有序性,优化器沿着索引的顺序扫描,在扫描到符合条件的M行数据后 ...

  6. Mysql之索引优化案例

    Mysql之索引优化案例 1.单表简单案例 1.1创建表 1.2 问题: 1.3 解决:新建索引 1.4 再次执行 2.双表简单案例 2.1创建表并插入数据 2.2 由于是LEFT JOIN,所以左表 ...

  7. mysql优化案例(14秒优化到不到1秒)

    优化案例 前面用过的tbiguser表有10000000条记录 创建tuser1表和tuser2表,并初始化若干的数据. create table tuser1( id int primary key ...

  8. MySQL第12天:MySQL索引优化分析之性能优化案例实践

    MySQL索引优化分析之性能优化案例实践 执行计划中各select_type含义可以看:MySQL第11天:MySQL索引优化分析之性能分析 https://weibo01.blog.csdn.net ...

  9. 【MySQL】故障分析 | MySQL 优化案例 - 字符集转换

    1.概述 好文章转载:故障分析 | MySQL 优化案例 - 字符集转换 一.背景 开发联系我,说是开发库上有一张视图查询速度很慢,9000 条数据要查 10s,要求我这边协助排查优化. 二.问题 S ...

  10. mysql半连接_MySQL优化案例:半连接(semi join)优化方式导致的查询性能低下(转载)...

    以下是来自DBA+社群MySQL领域原创专家李海翔分享的MySQL优化案例,关于MySQL V5.6.x/5.7.x SQL查询性能问题. 一.简单创建一表,并使用存储过程插入一部分数据 二.执行如下 ...

最新文章

  1. jQuery Datatables常用配置
  2. SpringBoot笔记:SpringBoot2.3集成Logback日志组件配置
  3. 使用IDEA创建一个Servlet应用程序
  4. 网络模型和TCP协议族
  5. 让机器有温度:带你了解文本情感分析的两种模型
  6. Python 变量 字符串 运算
  7. winform图片操作
  8. 红帽linux中文语言包,英文 RedHat AS5 中文语言包安装
  9. js 跨域获取cookie
  10. 考试酷c语言程序设计的答案大全,C语言程序设计考试试卷07级A.doc
  11. 《MLB美职棒大联盟》:MLB最佳阵容·MLB新年也要身体倍棒
  12. leaflet蜂巢图(leaflet篇.15)
  13. HBase-11-HBase Coprocessor HBase协处理器
  14. PyCharm 安装numpy包
  15. 设计模式 —— 简单工厂模式、工厂模式
  16. MLCS algorithm
  17. EDA软件兼容性哪家强?
  18. 从创业走偏,到完成5亿元C轮融资,黑湖智造凭什么?
  19. 计算机回收站设置大小,2010年职称计算机考试:“回收站”属性的设置
  20. 第二章 体重指数计算器

热门文章

  1. 微信html5切水果游戏,H5+JS切水果手机小游戏
  2. Linux根据端口号或者关键字查询进程,重启Tomcat服务脚本优缺点说明
  3. 5.3使用自定议的测试数据库文件
  4. 【渝粤教育】国家开放大学2019年春季 1344金融风险管理 参考试题
  5. Windows中绕过更新直接关机
  6. 自家公司关于git commit 的规范
  7. [转载]幂等和高并发在电商系统中的使用
  8. 项目实施流程和规范模板(测试方向)
  9. Android使用scrollview截取整个的屏幕并分享微信
  10. Looksery Cup 2015 B. Looksery Party 暴力