找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

478

积分

0

好友

62

主题
发表于 4 小时前 | 查看: 5| 回复: 0

“直接用 mysql limit 分页不就行了,分页查询用得着四种写法吗?”

这或许是不少开发者的第一反应。诚然,LIMIT OFFSET 是数据库分页最基础的实现方式,但在不同的业务场景下,其他方案往往能带来更优的性能与体验。

大家最熟悉的便是如下这种分页形式,它能够返回总页数并支持任意页数的跳转。 常见分页UI样式

Limit Offset分页

假设每页展示10条数据,查询第三页时,SQL中的 LIMIT 子句通常写作:LIMIT 20, 10;

前端请求需携带“每页数量”和“当前页码”参数,由后端拼接成SQL查询语句。

LIMIT OFFSET 分页具备两个显著特点:

  1. 支持随机跳页:用户可直接跳转到任意指定页面。
  2. 返回数据总量:便于前端展示总页数和总记录数。

尽管实现简单,LIMIT OFFSET 存在一个明显的性能缺陷:在深度分页(即 OFFSET 值非常大)时,MySQL 需要扫描并跳过大量数据才能定位到目标页,极易引发慢查询。这不仅会增加数据库的CPU与内存负载,若此类查询的QPS较高,还可能在高流量时段拖垮数据库,进而影响其他业务查询。

这里有一个重要原则:分页查询必须明确指定排序方式(ORDER BY。若未指定,则无法保证各页数据的一致性,可能出现记录重复。若无业务排序字段,使用主键ID排序是最稳妥的选择。笔者曾在从 Lucene 迁移至 ElasticSearch 时,因未指定排序导致分页结果不一致,排查良久才意识到此问题。

那么,LIMIT OFFSET 是否有方法规避深度分页问题呢?答案是肯定的。

Limit + 主键Id过滤

一种优化思路是在查询条件中引入主键ID进行范围过滤。

改进前:select * from students where [查询条件] order by id desc limit 1000, 20;
改进后:select * from students where [查询条件] AND id < lastMinId order by id desc limit 20;

优化后,我们在原有查询条件基础上,额外指定了 lastMinId(即上一页结果中最小的ID)。查询下一页时,将此值作为参数传入,确保只获取ID小于该值的后续记录。这相当于从源头缩小了检索范围,每次仅获取固定数量的数据。

这种方式是否就高枕无忧了?并非如此。

它的前提是排序字段必须为主键ID。若业务要求按其他字段(如时间、价格)排序,此法则不适用。

它同样适用于从数据源批量拉取全量数据的场景。通过按主键排序,并记录每次获取的最大或最小ID,进行连续批次查询,即可高效、无深度分页压力地获取全部数据。

HasMore 滚动查询

在某些用户端场景(如App内的购买记录列表),用户只需逐页浏览,无需知晓总订单数。针对此类“无限滚动”场景,可以进行优化。

传统 LIMIT OFFSET 方案需要执行一次 SELECT COUNT(*) 以获取总数。而滚动分页则无需此步骤。

具体实现是:每次查询时,请求的条数比实际展示数多1条。例如,每页展示10条,则查询11条。若实际返回11条,则设置 hasMore = true,提示前端还有更多数据;若返回条数≤10,则设置 hasMore = false,前端停止请求。这减少了一次昂贵的全表计数查询,提升了性能。

ElasticSearch 分页查询

ElasticSearch 适用于检索条件复杂、对实时性要求相对宽松的场景,例如B端后台的复杂筛选或C端的订单关键词搜索。这类查询耗时通常在百毫秒甚至秒级。

需要注意的是,ES的数据通常由MySQL等数据库/中间件异构同步而来,从数据产生到进入ES索引存在一定延迟(通常秒级)。

ES也支持类似 LIMIT OFFSET 的分页查询(通过 fromsize 参数),并且同样强烈建议在分页时指定排序方式

SearchRequest searchRequest = new SearchRequest(index);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 计算起始记录下标
int from = (pageNum - 1) * pageSize; // 起始记录下标,从0开始
sourceBuilder.from(from);
sourceBuilder.size(pageSize); // 每页显示的记录数

与MySQL类似,ES也存在深度分页的性能压力。其默认设置 max_result_window 为10000,即 from + size 不能超过此阈值。在低频的B端查询场景中,可根据需要适当调大此参数。

总结

以上四种分页方案各有优劣,没有绝对的“最佳”,关键在于根据具体的业务场景数据量级性能要求用户体验来选择最合适的那一种。




上一篇:嵌入式Linux开发实战:MCU到Linux的思维转变与关键差异解析
下一篇:前端高可靠架构:医疗级Web应用的实时通信设计与实践——从WS协议选型、容错到"零中断"保障
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区(YunPan.Plus) ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-6 23:54 , Processed in 0.102800 second(s), 37 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 CloudStack.

快速回复 返回顶部 返回列表