MySQL中limit对于order by的优化

一、 无limit的场景对于order by如何优化

考虑无limit的order by语句,下面假设age是一个普通索引。

1.1 查询无覆盖索引

select * from emp order by age;

此句中,查询的字段和order by后面的age并非在同一个索引中,因此如果走了索引,查询步骤应该是:

  1. 走age的索引,会带出排序后的数据的id
  2. 将这些id逐个回表查询,查询的次数和表长一样

MySQL优化器会认为查询数据量大,回表数据量也大,因此还不如直接全表记录扫描出来放到temp表中,然后sort一遍,这样就避免了多次回表。
在这里插入图片描述

1.2 查询覆盖索引

select age from emp order by age;

此句中,查询的字段age和order by的字段同属一个索引,因此优化器也会选择走索引。
在这里插入图片描述

二、limit对于order by语句的优化

我们知道,order by先于limit执行,因此优化器如果没有对order by和limit同时使用的场景的特殊优化,那么就会和无limit的情况一样去考虑,大多数情况下,会全表扫描。

但这并不是我们想要的,例如我们使用limit 10, 10这条语句,实际上只需要MySQL进行排序10 + 10次,然后将这20次回表即可,最后将排序的结果取出后面10条,就是我们要的结果,这个开销可能远小于全表扫描。

在mysql中,默认开启了prefer_ordering_index的选项,目的是在order by搭配limit的语句中,加入对于limit的考虑,如果权衡出来在offset + row_count的数据范围内,索引+回表的方式优于全表扫描,就会选择走索引的方式。

2.1 开启prefer_ordering_index(默认)

2.1.1. 走了索引

select * from emp order by age limit 10;

上面这句,优化器权衡后,发现只需要排序出前10个,回表10次,开销小,会走索引
在这里插入图片描述

2.1.2. 没走索引

select * from emp order by age limit 500;

上面这句,优化器权衡后,不走索引,选择全表扫描。

在这里插入图片描述

select * from emp order by age limit 5000, 10;

上面这句,实际上需要排序5000 + 10 = 5010条数据,回表5010次开销巨大,MySQL选择了全表扫描。
在这里插入图片描述

2.2. 关闭prefer_ordering_index

2.2.1. 关闭后优化器不管limit了

select * from emp order by age limit 1;

即使我们限制了结果集大小仅为1,回表开销很小,但是优化器只考虑order by的操作,忽视了limit,所以选择全表扫描。
在这里插入图片描述

2.3. 如何手动优化强制走索引

select * from emp order by age limit 100000, 10;

例如上述的场景,不管开不开prefer_ordering_index,都很有可能全表扫描,因为就算考虑了order by考虑进了limit,他也需要排序100000 + 10条数据,然后把后面10条拿出来,所以offset + row_count比较小的时候,才会自动走索引。
但是我们看中的是limit后面这个row_count比较小,但是大offset导致了order by需要排序回表负担过大。
实际上我们可以使用子查询,先用覆盖索引走查出这10个age,然后再和大表连接,这样只会有10次回表。

1. 错误做法

select * from emp where id in (select id from emp order by age limit 1);

MySQL不支持in中写limit的写法。

2. 正确做法:连接子查询表

select a.* from emp a, (select id from emp order by age) b where a.id = b.id;

在这里插入图片描述

三、limit对于order by提前退出的优化

limit [offset, ] row_count

MySQL 只要排序到能够确定了这 row_count 行结果,就会停止排序,而不是整个排序。换言之,他只会排序offset + row_count个数据。

-- 如果我们只需要找到emp中工资最高的2个员工的信息
select * from emp order by salary desc limit 2;

假设salary没索引,选择全表排序,这种情况下,并不会对所有数据根据salary排序,而是选出最大的两个。我们知道,选出最大两个的复杂度是O(N),而全表排序,就不是这个复杂度了。

参考资料

https://dev.mysql.com/doc/refman/5.7/en/limit-optimization.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQLLIMITORDER BY可以结合使用来限制返回结果集的行数并对结果进行排序。ORDER BY用于对查询结果进行排序,可以按照指定的列或表达式进行升序或降序排序。而LIMIT用于限制返回的结果集的行数,可以指定返回的起始位置和要返回的行数。通过结合使用ORDER BY和LIMIT,可以对排序后的结果进行切片操作,只返回指定数量的行。需要注意的是,如果ORDER BY的列有相同的值,MySQL可以自由地以任何顺序返回这些行,只要ORDER BY的列的值不重复,就可以保证返回的顺序。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [MySQLORDER BY 可以和 LIMIT 混合使用吗?底层原理是什么?](https://blog.csdn.net/qq_36777143/article/details/131167778)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [MySQLORDER BY与LIMIT一起使用(有坑)](https://blog.csdn.net/wit_cx/article/details/109212690)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值