MySQL分页使用limit和offset参数,为什么会导致执行变慢?

仔细分析一下,会发现通过索引去找很别扭。因为你不知道前100个数在左子树和右子数的分布情况,所以其是无法利用二叉树的查找特性。通过学习,了解到mysql的索引是b+树。看了这个图,就豁然开朗了。可以直接通过叶子节点组成的链表,以o(n)的复杂度找到第100大的树。但是即使是o(n),也不至于慢得...
MySQL分页使用limit和offset参数,为什么会导致执行变慢?
从一个问题说起

五年前在tx的时候,发现分页场景下,mysql请求速度非常慢。数据量只有10w的情况下,select xx from 单机大概2,3秒。我就问我导师为什么,他反问“索引场景,mysql中获得第n大的数,时间复杂度是多少?”

答案的追寻确认场景

假设status上面有索引。select * from table where status = xx limit 10 offset 10000。会非常慢。数据量不大的情况就有几秒延迟。

小白作答

瞎猜了个log(N),心想找一个节点不就是log(N)。自然而然,导师让我自己去研究。

这一阶段,用了10分钟。

继续解答

仔细分析一下,会发现通过索引去找很别扭。因为你不知道前100个数在左子树和右子数的分布情况,所以其是无法利用二叉树的查找特性。通过学习,了解到mysql的索引是b+树。

看了这个图,就豁然开朗了。可以直接通过叶子节点组成的链表,以o(n)的复杂度找到第100大的树。但是即使是o(n),也不至于慢得令人发指,是否还有原因。

这一阶段,主要是通过网上查资料,断断续续用了10天。

这里有两个关键概念:

即使前10000个会扔掉,mysql也会通过二级索引上的主键id,去聚簇索引上查一遍数据,这可是10000次随机io,自然慢成哈士奇。

这里可能会提出疑问,为什么会有这种行为,这是和mysql的分层有关系,limit offset 只能作用于引擎层返回的结果集。换句话说,引擎层也很无辜,他并不知道这10000个是要扔掉的。以下是mysql分层示意图,可以看到,引擎层和server层,实际是分开的。

直到此时,大概明白了慢的原因。这一阶段,用了一年。

触类旁通

此时工作已经3年了,也开始看一些源码。在看完etcd之后,看了些tidb的源码。无论哪种数据库,其实一条语句的查询,是由逻辑算子组成。

逻辑算子介绍 在写具体的优化规则之前,先简单介绍查询计划里面的一些逻辑算子。

选择,投影,连接(简称 SPJ) 是最基本的算子。其中 Join 有内连接,左外右外连接等多种连接方式。更多MySQL面试题,面试宝典: yoodb.com

select b from t1, t2 where t1.c = t2.c and t1.a > 5 变成逻辑查询计划之后,t1 t2 对应的 DataSource,负责将数据捞上来。上面接个 Join 算子,将两个表的结果按 t1.c = t2.c连接,再按 t1.a > 5 做一个 Selection 过滤,最后将 b 列投影。下图是未经优化的表示:

所以说不是mysql不想把limit, offset传递给引擎层,而是因为划分了逻辑算子,所以导致无法直到具体算子包含了多少符合条件的数据。

怎么解决方案一

根据业务实际需求,看能否替换为下一页,上一页的功能,特别在ios, android端,以前那种完全的分页是不常见的。这里是说,把limit, offset,替换为>辅助索引(即搜索条件)id的方式。该id再调用时,需要返回给前端。

方案二

正面刚。这里介绍一个概念:索引覆盖:当辅助索引查询的数据,只有id和辅助索引本身,那么就不必再去查聚簇索引。

思路如下:select xxx,xxx from in (select id from table where second_index = xxx limit 10 offset 10000) 这句话是说,先从条件查询中,查找数据对应的数据库唯一id值,因为主键在辅助索引上就有,所以不用回归到聚簇索引的磁盘去拉取。再通过这些已经被limit出来的10个主键id,去查询聚簇索引。这样只会十次随机io。在业务确实需要用分页的情况下,使用该方案可以大幅度提高性能。通常能满足性能要求。

写在最后

从一个小问题,往下深究,是技术的一个提升捷径。2024-11-10
mengvlog 阅读 10 次 更新于 2025-07-21 00:42:50 我来答关注问题0
  •  翡希信息咨询 谈谈MySQL的limit用法、逻辑分页和物理分页

    一、MySQL的limit用法 基本语法:LIMIT X, Y,表示跳过前X条数据,读取接下来的Y条数据。用途:常用于实现分页查询,通过调整X和Y的值来获取不同页面的数据。优化建议:在大数据量场景下,单纯使用LIMIT可能导致效率下降。为提高效率,可以结合索引和WHERE条件进行优化,避免全表扫描。二、逻辑分页 概念:...

  •  深空见闻 MySQL数据库limit分页、排序-SQL语句示例

    MySQL数据库中使用LIMIT进行分页和排序的SQL语句示例如下:基本的分页查询:语法:SELECT * FROM 表名 LIMIT 起始位置, 返回行数;示例:SELECT * FROM persons LIMIT 0, 4;:从第0条记录开始,返回4条记录。这是查询前4条记录。SELECT * FROM persons LIMIT 4, 4;:从第5条记录开始(因为索引从0...

  •  文暄生活科普 谈谈MySQL的limit用法、逻辑分页和物理分页

    在物理分页中,limit通常有两个参数:X与Y。X表示跳过X个数据,Y表示读取Y个数据。SQL查询与java实现分页时,可以设定页数与每页显示行数的方法。综上所述,合理使用limit与分页策略,结合索引优化,能有效管理大数据处理的效率与资源使用。

  •  文暄生活科普 MySQL 使用 limit 分页会导致数据丢失、重复和索引失效

    MySQL 使用 limit 分页在某些情况下确实可能导致数据丢失、重复和索引失效,具体分析及解决方案如下:数据丢失问题:原因:在使用 limit 进行分页查询时,如果排序字段不具有唯一性,可能会导致分页结果中的数据量减少,即数据丢失。解决方案:在排序字段中添加唯一值,如主键ID,以确保分页查询的准确性和完整...

  •  翡希信息咨询 MySQL数据库limit分页、排序-SQL语句示例

    MySQL数据库limit分页、排序的SQL语句示例如下:基本的分页查询:使用LIMIT关键字进行分页查询,其中A表示查询的起点位置,B表示需要返回的行数。示例:SELECT * FROM persons LIMIT 0, 4;解释:从起点位置0开始查询,返回4条数据。示例:SELECT * FROM persons LIMIT 4, 4;解释:从起点位置4开始查询,...

檬味博客在线解答立即免费咨询

mySQL相关话题

Copyright © 2023 WWW.MENGVLOG.COM - 檬味博客
返回顶部