MySQL - order by 出现 using filesort根因分析及优化
文章目录
- Pre
- Case
- table
- 模拟数据
- 故障复现
- 咋办?
- 方案一 加limit ,少取一点?
- using filesort 到底是个什么鬼????
- filesort 步骤
- rowid 排序 ?
- 不想回表? 也不想排序?
- 温馨提示
- 验证
- 回顾总结
Pre
MySQL - order by和 group by 优化初探
Case
table
CREATE TABLE `user` (`id` int(10) AUTO_INCREMENT,`user_id` int(10),`gf_phone` varchar(1000),`gf_name` varchar(100), PRIMARY KEY (`id`),KEY `user_id` (`user_id`)
) ENGINE=InnoDB;
- 用户的 user_id ,女朋友的名字gf_name、女朋友的电话号码gf_phone
- user_id 索引
模拟数据
# 清空表
truncate table user;
# 删除存过
drop PROCEDURE batchInsert # 创建存储过程
delimiter ‘$’;
CREATE PROCEDURE batchInsert(in args int)
BEGIN
declare i int default 1;
start TRANSACTION;
while i <= args DO
insert into user(id,user_id, gf_name ,gf_phone) VALUES ( i, 8888, concat("art-" , i),i);
set i = i+1;
end while;
COMMIT;
end# 批量写入数据 100万call batchInsert(1000000);
故障复现
select gf_name,gf_phone from user where user_id=8888 order by gf_name ;
还等啥, explain 呀
咋办?
看看这个SQL的数据结果集有多少???? ------100万
方案一 加limit ,少取一点?
当然了实际工作中是基本不会出现这种情况的, 假设真的取了100万数据, 无论是MySQL内存缓冲区的占用,还是网络带宽的消耗都是巨大的。
那加了limit 10呢?因为数据包整体变小了, 网络带宽的问题是解决了,但是 using filesort 的问题并没有解决,mysql还是要给你排序的呀。
using filesort 到底是个什么鬼????
让我大胆的猜一猜啊
在文件中排序的?在文件中到底是怎么排序的?
我们分析一下现在的索引和数据情况哈
首先 user_id 是有索引的,所以会先在 user_id 索引树上检索目标数据,即
user_id=8888
的数据,但是我们要查询的是gf_name 和gf_phone 字段, 光靠 user_id 索引是找不到这两个字段值的mmp 100万个女朋友。。。。
所以啊,找不到怎么办????? 需要回表,通过 user_id 对应的主键去【主键索引树】上去查找,还真他娘的找到了第一条 user_id=8888 的gf_name 和 gf_phone 字段 . 可是我有100万个女朋友啊。。。。。
怎么办,直接返回???? 可是你妹的还让我
order by gf_name
,按女朋友的名字给你排个序呀???? 数据都还没找全,那么就得把查到的数据先放在一个地方,这就是 sort_buffersort_buffer 是用于这种情况下排序用的缓冲区,这里需要注意的是每个线程都会有一个单独的 sort_buffer,这么做的目的主要是为了避免多个线程对同一块内存进行操作带来锁竞争的问题。
当第一条数据的 gf_name 和 gf_phone 已经放入 sort_buffer 中,这肯定不能结束呀,会一直重复同步的步骤,直至把所有 user_id=8888 的 gf_name 和 gf_phone 都放入到 sort_buffer 中才结束.
sort_buffer 中的数据已经放入完毕,接下来就该排序了,这里 MySQL 会对 gf_name 进行快排,通过快排后,sort_buffer 中 gf_name 就是有序的了
- 最后返回 sort_buffer 中的limit 条数,结束。
就是这么顺畅啊。。。。。
整个过程类似如下 :
sort_buffer 占用的是内存空间,它有上限的, 不能太小,又不能太大。
在 InnoDB 存储引擎中,这个值是默认是256K。
那如果要放进 sort_buffer 中的数据是大于256K的话,那么采用在 sort_buffer 中快排的方式肯定是不行的啊。
那MySQL难道不能根据数据大小自动扩充吗?
MySQL是多线程模型,如果每个线程都扩充,那么分给其他功能buffer就小了(比如change buffer等等等等呢个),就会影响其他功能了,所以,mysql采用了另外一种方式处理 。
filesort 步骤
此时就是真正的文件排序了,也就是磁盘的临时文件,MySQL会采用归并排序的思想,把要排序的数据分成若干份,每一份数据在内存中排序后会放入临时文件中,最终对这些已经排序好的临时文件的数据再做一次合并排序就ok了. 这不就是分治的思想嘛 。
具体步骤如下
- 先将要排序的数据分割,分割成每块数据都可以放到 sort_buffer 中
对每块数据在 sort_buffer 中进行排序,排序好后,写入某个临时文件中
当所有的数据都写入临时文件后,这时对于每个临时文件而言,内部都是有序的,但是它们并不是一个整体,整体还不是有序的,所以接下来就得合并数据
- 假设现在存在 tmpX 和 tmpY 两个临时文件,这时会从 tmpX 读取一部分数据进入内存,然后从 tmpY 中读取一部分数据进入内存
为什么是一部分而不是整个或者单个?因为首先磁盘是缓慢的,所以尽量每次多读点数据进入内存,但是不能读太多,因为还有 buffer 空间的限制。
- 对于 tmpX 假设读进来了的是 tmpX[0-5] ,对于 tmpY 假设读进来了的是 tmpY[0-5],于是只需要这样比较:如果 tmpX[0] < tmpY[0],那么 tmpX[0] 肯定是最小的,然后 tmpX[1] 和 tmpY[0] 比如,如果 tmpX[1] > tmpY[0],那么 tmpY[0] 肯定是第二小的…,就这样两两比较最终就可以把 tmpX 和 tmpY 合并成一个有序的文件tmpZ,多个这样的tmpZ再次合并…,最终就可以把所有的数据合并成一个有序的大文件。
rowid 排序 ?
看完了上面的排序流程 , 如果要排序的数据很大,超过 sort_buffer 的大小,那么就需要文件排序,文件排序涉及到分批排序与合并,很耗时。
为什么呢? sort_buffer 不够用啊
那就两个思路
- 扩 --------------> mysql不支持,甭想了
- 缩 ---------------> 少向sort buffer中放一点,这样就能多存点了
我们分析下SQL
select gf_name,gf_phone from user where user_id=8888 order by gf_name
我们需要按照 gf_name 排序,但是却把 gf_phone 也塞进了 sort_buffer 中
这样 单行数据的大小就等于 gf_name 的长度 + gf_phone 的长度 ,能否让 sort_buffer 中只存 gf_name 字段,这样的话,整体的利用空间就大了,说不定不一定用得到到临时文件, 哈哈哈哈
是的,这就是另外一种排序优化 rowid排序
rowid 排序的思想就是把不需要的数据不要放到 sort_buffer 中,让 sort_buffer 中只保留必要的数据。
只放 gf_name 可以吗 ?
SQL: select gf_name,gf_phone
排序完了之后, 要获取gf_phone怎么办?
因此还要把主键id放进去,这样排完之后,通过 id 再回次表,拿到 gf_phone即可.
大致流程如下:
1. 根据 user_id 索引,查到目标数据,然后回表,只把 id 和 gf_name 放进 sort_buffer 中
2. 重复1步骤,直至全部的目标数据都在 sort_buffer 中
3. 对 sort_buffer 中的数据按照 gf_name 字段进行排序
4. 排序后根据 id 再次回表查到 gf_phone 返回,直至返回1000条数据,结束。
有几点需要注意的:
- 需要两次回表
- sort_buffer 虽然小了,但是如果数据量本身还是很大,应该还是要临时文件排序的\
MySQL 该如何选择?得根据某个条件来判断走哪种方式吧,这个条件就是进 sort_buffer 单行的长度,如果长度太大(gf_name+ gf_phone的长度),就会采用 rowid 这种方式,否则第一种,长度的标准是根据 max_length_for_sort_data
来的,这个值默认是1024字节:
不想回表? 也不想排序?
不管是上面哪种方法,我们发现其实是都需要 回表 + 排序的
回表是因为二级索引上没有目标字段
排序是因为数据不是有序的
那如果二级索引上有目标字段并且已经是排序好的了,那不就OK了嘛
联合索引 搞一搞嘛
只需要建立一个 (user_id,gf_name,gf_phone)
的联合索引即可,这样就可以通过这个索引拿到目标数据,并且gf_name已经是排序好的,同时还有gf_phone字段, 不需要回表,不需要再次排序。
流程大致如下:
通过联合索引找到user_id=8888的数据,然后读取对应的 gf_name和 gf_phone字段直接返回,因为 gf_name已经是排序好的了,不需要额外处理
重复第一步骤,顺着叶子节点接着向后找,直至找到第一个不是8888的数据,结束。
温馨提示
小提示: 联合索引虽然可以解决这种问题,但是在实际应用中切不可盲目建立,要根据实际的业务逻辑来判断是否需要建立,如果不是经常有类似的查询,可以不用建立,因为联合索引会占用更多的存储空间和维护开销。
验证
ALTER TABLE `artisan`.`user`
DROP INDEX `user_id`,
ADD INDEX `idx_name_phone`(`user_id`, `gf_name`, `gf_phone`);
业务系统,一般我们都得加limit呀。谁会取100万出来呢?
看看执行计划吧
回顾总结
对于 order by 没有用到索引的时候,这时 explain 中 Extra 字段大概是会出现 using filesort 字眼
出现 using filesort 的时候也不用太慌张,如果本身数据量不大,比如也就几十条数据,那么在 sort buffer 中使用快排也是很快的
如果数据量很大,超过了 sort buffer 的大小,那么是要进行临时文件排序的,也就是归并排序,这部分是由 MySQL 优化器决定的
如果查询的字段很多,想要尽量避免使用临时文件排序,可以尝试设置下
max_length_for_sort_data
字段的大小,让其小于所有查询字段长度的总和,这样放入或许可以避免,但是会多一次回表操作实际业务中,我们也可以给经常要查询的字段组合建立个联合索引,这样既不用回表也不需要单独排序,但是联合索引会占用更多的存储和开销
大量数据查询的时候,尽量分批次,提前 explain 来观察 sql 的执行计划是个不错的选择。
MySQL - order by 出现 using filesort根因分析及优化相关推荐
- mysql的explain的用途,利用Explain来分析和优化你的mysql
下面我用一个例子来演示如何使用explain来优化mysql查询: 需求是这样的,在一个有1300+万条的mysql表中查出一个时间段内的数据,联表查询:select d2.name as '大区' ...
- MySQL Order by 语句用法与优化详解
Order by语句是用来排序的,经常我们会使用到Order by来进行排序,下面我给大家来讲讲Order by用法与优化排序,有需要的同学可参考 MySQL Order By keyword是用来给 ...
- mysql div 没有小数,mysql order by limit 的一个坑
mysql order by limit 的一个坑 分页查询的时候遇到的坑: 发现的问题: 对单个无索引的字段进行排序后limit .发现当被排序字段有相同值时并且在limit范围内,取的值并不是正常 ...
- mysql order 关键字_PHP数据库MySQL Order By 关键词 - PHP教程
PHP MySQL Order By 关键词 你可以对 MySQL 数据库中的记录集进行排序,具体请阅读本节内容. ORDER BY 关键词用于对记录集中的数据进行排序. ORDER BY 关键词 O ...
- mysql order by 中文 排序
mysql order by 中文 排序 1. 在MySQL中,我们经常会对一个字段进行排序查询,但进行中文排序和查找的时候,对汉字的排序和查找结果往往都是错误的. 这种情况在MySQL的很多版本中都 ...
- mysql order by 索引名字_MySQL如何利用索引优化ORDER BY排序语句
MySQL索引通常是被用于提高WHERE条件的数据行匹配或者执行联结操作时匹配其它表的数据行的搜索速度. MySQL也能利用索引来快速地执行ORDER BY和GROUP BY语句的排序和分组操作. 通 ...
- Mysql Order By 字符串排序,mysql 字符串order by
Mysql Order By 字符串排序,mysql 字符串order by ============================== ©Copyright 蕃薯耀 2017年9月30日 http ...
- MySql Order By 多个字段 排序规则
说在前面 突发奇想,想了解一下mysql order by排序是以什么规则进行的? 好了,话不多说,直接进入正题吧. MySql order by 单字段 建一测试表如下: CREATE TABLE ...
- MySQL order by基于不同字段条件排序
MySQL order by基于不同字段条件排序 从数据库上查询数据的时候,按要求排序,在某个字段的不同值的基础上再选择不同的字段进行排序,具体描述如下: 1:首先有一个基础字段status ,sta ...
最新文章
- php回调函数原理和实例
- 全站仪数据导入电脑_三鼎762R系列全站仪的SD卡传输教程
- twitter数据集_大数据周报-201925
- 小数前的0在html不显示,jsp小数显示问题 例如 我在oracle 数据库中查询出来的是 0.01 但是在jsp页面上就显示成 .01 没有前面的0...
- 中resource文件夹的作用_冲突与碰撞:OpenStack中的虚拟机和裸机
- 七度空间338多少钱一包_2020黄果树香烟一包多少钱 黄果树香烟价格表图排行榜...
- python数据驱动测试_python数据驱动--Excel维护测试用例
- 7-3 sdut-求两个整数之和(I)
- loadrunner提示:Cannot save the license information because acceses to the registry is denied
- 听说去了BAT的 Linuxers 都做过这套面试题!
- docker重启容器
- 我的世界java边境之地_《我的世界》:手机版的边境之地你绝对没见过!那里方块只有空壳?...
- 从音箱入门到高手必看知识
- stc15w4k32s4最小系统原理图_单片机最小系统原理图,求详细讲解
- html img 能显示psd吗_psd变成html
- 微信小程序开发05 研发加速:使用 Webpack 提升小程序研发效率
- 011 dilate(膨胀)、erode(腐蚀)
- open函数里的newline
- vue-cli4引入Element Plus 插件
- 宠物店 java 报告_宠物店社会实践报告通用范文
热门文章
- XX公司数据库迁移项目可行性研究与风险评估
- Java人民币从数字形式转换成汉字(繁体)!
- 敬畏风险成用户见面会主题 小赢科技全面高垒安全护城河
- python怎么筛选excel数据_工具篇:EXCEL与python的对比 如何筛选数据.md
- 【PMSM控制】电流环带宽
- IDEA中的facets和artifacts的介绍
- 三星 S9+突然黑屏,如何解决
- 双非一本网易测试实习入职感受,难以想象的“平和”
- 2022年一季度全球员工敬业度降至62%,员工流失、经理倦怠仍是全球挑战 | 美通社头条...
- 见证奇观(重庆日全食,最美、最好)