首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >MySQL 查询语句的 limit, offset 是怎么实现的?

MySQL 查询语句的 limit, offset 是怎么实现的?

作者头像
csch
发布于 2022-09-05 02:04:11
发布于 2022-09-05 02:04:11
2.2K00
代码可运行
举报
文章被收录于专栏:一树一溪一树一溪
运行总次数:0
代码可运行

在写 select 语句的时候,使用 limit, offset 可能就像是我们吃饭喝水一样自然了。

刚开始工作的时候也经常听前辈们教导:使用 limit, offset,当 offset 变大的时候执行效率会越来越低。

相信在前辈们的言传身教,和自己的实战过程中,大家也都知道了为什么会这样。

因为 select 在执行过程中,对于存储引擎返回的记录,经过 server 层的 WHERE 条件筛选之后,符合条件的前 offset 条记录,会被直接无情的抛弃,直到符合条件的第 offset + 1 条记录,才开始发送给客户端,发送了 limit 条记录之后,查询结束。

虽然知道了是什么,也知道了为什么,但是我也一直好奇底层是怎么实现的,所以今天我们来扒一扒它的庐山真面目。

1. 语法回顾

先来简单的回顾一下 select 语句中 limit, offset 的语法,MySQL 支持 3 种形式:

  • LIMIT limit: 因为没有指定 offset,所以 offset = 0,表示读取符合 WHERE 条件的第 1 ~ limit 条记录。
  • LIMIT offset, limit: 我们常用的就是这种了。
  • LIMIT limit OFFSET offset: 这种不常用。

offset 和 limit 的值都不能为负数,在源码里这两个属性定义的是无符号整数,并且在解析阶段就做了限制,如果为负数,直接报语法错误了。

2. 语法解析阶段

在读取数据的过程中,对于符合条件的前 offset 条记录,会直接忽略,不发送给客户端,从符合条件的第 offset + 1 条记录开始,发送 limit 条记录给客户端。

所以,server 层实际上需要从存储引擎读取 offset + limit 条记录,源码里也是这么实现的,语法解析阶段,在验证了 offset 和 limit 都是大于等于 0 的整数之后,就把 offset + limit 的计算结果保存到一个叫做 select_limit_cnt 的属性里,offset 也会保存到一个叫做 offset_limit_cnt 的属性里。

3. 发送数据阶段

来到发送数据阶段,此时的记录已经通过了 WHERE 条件的筛选,接下来就是判断这条记录是不是要发送给客户端。

第 1 步

因为 offset 已经保存到 offset_limit_cnt 中了,先来判断 offset_limit_cnt 是否大于 0,如果大于 0,这条记录就会被抛弃了,不发送给客户端;如果等于 0,记录就具备了发送给客户端的资格了,然后接着进入第 2 步

在抛弃记录之前,还会干一件事:对一个叫做 send_records 的属性进行加 1 操作,就是假装这条记录已经发送了(为什么这样干?第 2 步会用到这个属性)。

offset_limit_cnt 是保证不会小于 0 的,所以在这一步只需要判断是大于 0 还是等于 0 就可以了。

第 2 步

来到这一步,记录就具备了发送给客户端的资格了,至于要不要发,就看客户端想不想要它了,而客户端想不想要它,取决于 select_limit_cnt

所以,在这一步要判断已发送记录数量(send_records)和需要发送记的录数量(select_limit_cnt)之间的关系,如果已发送记录数量大于等于需要发送的记录数量,则结束查询,否则就接着进入第 3 步。

第 3 步

在这里,记录愉快的等待着被发送给客户端。

是的,还要愉快的等着,因为要排队,毕竟运输也是需要成本的,不能来一条记录,就发一趟车,要等一辆车装满之后,才会发车的。这里的指的是网络缓冲区,以后也会写文章介绍,敬请期待。

4. 最佳实践

既然在 offset 变大之后,使用 limit, offset 效率越来越低,那应该怎么办呢?对于实战经验丰富的小伙伴来说,这是相当简单了,但是以防万一刚看到本文的小伙伴是刚刚开始用 SQL 写 Bug,所以还是要大概的写一下的。

以一个 SQL 为例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select * from t2
where i1 > 90000000 limit 8888, 10

为了取到 10 条记录,要先找到 8888 条记录,然后取到需要的 10 条,前面 8888 条记录都白找了,太浪费了,可以这样修改一下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select * from t2 
where i1 > 90000000 and id > LAST_MAX_ID
limit 10

LAST_MAX_ID 是上一次执行 SQL 时读取到的主键 ID 的最大值,如果是第一次执行语句,LAST_MAX_ID = 0

不过这种方案也有个问题,不支持跳着翻页,只支持顺序翻页(就是每次都点下一页的这种)。

如果要支持跳着翻页,怎么办?

只用 MySQL 这把锤子显然有点不够用了,还要再找一把锤子(Redis),可以把符合条件的记录的主键 ID 都读取出来,存入到 Redis 的有序集合(zset)中,用 zset 相应的函数读取到某一页应该展示的数据对应的那些主键 ID,然后用这些主键 ID 去 MySQL 中查询对应的数据,从而用两把锤子间接的实现了分页功能。

当然,这个方案也是有适用场景的,比如,这个方案明显就不适用于这些场景:符合条件的记录非常非常多导致存主键 ID 到 Redis 要占用很大的内存、记录更新频繁导致存主键 ID 的缓存经常被清除。如果碰到更复杂的场景,就要结合业务具体情况具体分析了。

以上就是本文全部内容了,给坚持看到的这点的朋友点个赞 ^_^

预告一下,接下来会写一篇不带 WHERE 条件的查询语句的执行过程,敬请期待!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一树一溪 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
limit offset慢查询背后的原因与解法
这样写看起来很正常,但实际在数据量大了之后,使用起来开始出现问题,越来越慢,慢到不可接受,甚至影响其他的读写操作。
Cloudox
2022/03/24
2.4K0
limit offset慢查询背后的原因与解法
带你读 MySQL 源码:limit, offset
MySQL 源码数量庞大,各种功能的代码盘根错节,相互交织在一起,形成一张复杂的网。
csch
2023/05/24
1.1K0
带你读 MySQL 源码:limit, offset
mysql查询 limit 1000,10 和limit 10 速度一样快吗?如果我要分页,我该怎么办?
建表sql大家也不用扣细节,只需要知道id是主键,并且在user_name建了个非主键索引就够了,其他都不重要。
小白debug
2022/06/20
1.8K0
mysql查询 limit 1000,10 和limit 10 速度一样快吗?如果我要分页,我该怎么办?
要想通过面试,MySQL的Limit子句底层原理你不可不知
  在二级索引idx_key1中,key1列是有序的,查找按key1列排序的第1条记录,MySQL只需要从idx_key1中获取到第一条二级索引记录,然后直接回表取得完整的记录即可,这个很容易理解。
砖业洋__
2023/05/06
5540
要想通过面试,MySQL的Limit子句底层原理你不可不知
《高性能 MySQL》读书笔记
1、隔离级别有四种: READ UNCOMMITTED(未提交读),同事务中某个语句的修改,即使没有提交,对其他事务也是可见的。这个也叫脏读。 READ COMMITTED(提交读),另一个事务只能读到该事务已经提交的修改,是大多数据库默认的隔离级别。但是有下列问题,一个事务中两次读取同一个数据,由于这个数据可能被另一个事务提交了两次,所以会出现两次不同的结果,所以这个级别又叫做不可重复读。这里的不一样的数据包括虚读(两次结果不同)和幻读(出现新的或者缺少了某数据)。 REPEATABLE READ(可重复读),这个级别不允许脏读和不可重复读,比如MYSQL中通过MVCC来实现解决幻读问题。 SERIALIABLE(可串行化),这儿实现了读锁,级别最高。
DannyHoo
2018/09/13
1.6K0
《高性能 MySQL》读书笔记
灵魂两问:MySQL分页有什么性能问题?怎么优化?
在这种建表语句中不用过度注重细节,只需要知道 id 是主键,并且在user_name建了一个非主键的索引就行了。
xiao李
2024/02/03
9330
灵魂两问:MySQL分页有什么性能问题?怎么优化?
执行一条 SQL 语句,期间发生了什么?
学习 SQL 的时候,大家肯定第一个先学到的就是 select 查询语句了,比如下面这句查询语句:
小林coding
2022/10/27
8990
执行一条 SQL 语句,期间发生了什么?
MySQL 案例:Limit 分页查询优化
在各类系统的表格类信息展示的功能中,经常会用到“翻页”这个操作,在页面上每次只展示有限的数据,需要看其他数据的时候则像翻书一样翻到后面的“页”。在 MySQL 支持的 SQL 语法中对此有特殊的支持,开发人员在实现这类功能的时候很方便:
王文安@DBA
2020/07/28
4.2K0
MySQL 案例:Limit 分页查询优化
MySQL调优之查询优化
一般情况下,查询可以看成按如下顺序执行任务:由客户端向服务端发起查询请求,然后在服务器端进行解析,生成执行计划,执行,最后将结果返回给客户端。
行百里er
2020/12/02
1.2K0
MySQL调优之查询优化
MySQL 简单查询语句执行过程分析(五)发送数据给客户端
本文是 MySQL 简单查询语句执行过程分析 6 篇中的第 5 篇,第 1 ~ 4 篇请看这里: 1. 词法分析 & 语法分析 2. 查询准备阶段 3. 从 InnoDB 读数据 4. WHERE 条件
csch
2022/09/05
1.6K0
MySQL 简单查询语句执行过程分析(五)发送数据给客户端
MySQL Limit实现原理
在实际工作中,我们经常会使用 MySQL 中的LIMIT子句来控制查询返回的数据大小,特别是在分页、性能优化等场景中。这篇文章,我们将深入探讨 MySQL 中LIMIT的实现原理,以及如何在不同场景下有效利用该功能。
程序猿川子
2024/12/02
4270
MySQL Limit实现原理
分页场景(limit, offset)为什么会慢?
来源 | https://juejin.cn/post/6844903939247177741
程序猿DD
2021/04/20
1.3K0
MySQL Optimization 优化原理
如果能在头脑中构建一幅MySQL各组件之间如何协同工作的架构图,有助于深入理解MySQL服务器。下图展示了MySQL的逻辑架构图。
吴生
2018/04/04
1.2K0
MySQL Optimization 优化原理
求求你不要再用offset和limit了
随着时代的发展,每个新企业家都希望建立下一个Facebook,并结合收集每个可能的数据点以提供更好的机器学习预测的心态,作为开发人员,我们需要比以往更好地准备我们的API,以提供可靠,高效的端点,应该能够毫不费力地浏览大量数据。
JavaPub
2021/01/11
1.4K1
高性能MySQL(4)——查询性能优化
在尝试编写快速的查询之前,需要清楚一点,真正重要是响应时间。如果把查询看作是一个任务,那么他由一系列子任务组成,每个子任务都会消耗一定的时间。如果要优化查询,实际上要优化其子任务,要么消除其中一些子任务,要么减少子任务的执行的次数,要么让子任务运行得更快。
栗筝i
2022/12/01
1.6K0
高性能MySQL(4)——查询性能优化
关于mysql limit offset的一点优化
举个例子select * from test where val=4 limit 300000,5;的查询过程:
MickyInvQ
2020/09/27
9.5K0
关于mysql limit offset的一点优化
我的Mysql查询SQL优化总结
当我们遇到一个慢查询语句时,首先要做的是检查所编写的 SQL 语句是否合理,优化 SQL 语句从而提升查询效率。所以对 SQL 有一个整体的认识是有必要的。
程序员小明
2019/10/10
2K0
我的Mysql查询SQL优化总结
分页查询 offset 和 limit 和 limit 的区别
select * from table limit 0,1000; select * from table limit 1000 offset 0;
袁新栋-jeff.yuan
2020/08/26
3.2K0
MySQL:为什么用limit时,offset很大会影响性能
我们知道,当limit offset rows中的offset很大时,会出现效率问题:
乔戈里
2019/12/24
1.5K0
mysql之存储引擎 体系结构 查询机制(二)
1,插拔式的插件方式 ,插拔式的插件方式 2,存储引擎是指定在表之上的,即一个库中的每一个表都可 ,存储引擎是指定在表之上的,即一个库中的每一个表都可以指定专用的存储引擎。 3,不管表采用什么样的存储引擎,都会在数据区,产生对应 ,不管表采用什么样的存储引擎,都会在数据区,产生对应的一个 的一个frm文件(表结构定义描述文件)
周杰伦本人
2022/10/25
9240
mysql之存储引擎 体系结构 查询机制(二)
相关推荐
limit offset慢查询背后的原因与解法
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档