当前位置: 首页 > news >正文

高效分页策略:掌握 LIMIT 语句的正确使用方法与最佳实践

本文主要介绍limit 分页的弊端及线上应该怎么用

LIMIT M,N

平时经常见到使用 <limit m,n>+ 合适的 order by 来实现分页查询,这样做到底性能如何呢?

先来简单分析下,然后再实际验证一下。

  1. 无索引条件下,需要做大量的文件排序操作,性能将会非常糟糕;
  2. 有索引条件下,刚开始的分页查询效率会比较理想,但越往后,分页查询的性能就越差。

这主要是因为,在使用 LIMIT 的时候,偏移量 M 在分页越靠后的时候,值就越大,数据库检索的数据也就越多。
例如 LIMIT 90000,10 这样的查询,数据库需要查询 90010 条记录,最后返回 10 条记录。也就是说将会有 90000 条记录被查询出来没有被使用到。

下面我们来验证下
首先创建一张会员表,表结构如下

CREATE TABLE `member` (`id` int(11) NOT NULL AUTO_INCREMENT,`member_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,`member_phone` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`join_date` datetime DEFAULT CURRENT_TIMESTAMP,`member_id` bigint(20) NOT NULL,PRIMARY KEY (`id`),UNIQUE KEY `idx_member_id` (`member_id`)
)

插入 10 万条数据

DELIMITER //CREATE PROCEDURE InsertMember()
BEGINDECLARE i INT DEFAULT 0;WHILE i < 100000 DO-- 为member_id生成一个10位随机数SET @random_member_id = FLOOR(RAND() * 9000000000 + 1000000000) + i*RAND();-- 插入数据INSERT INTO member (member_name, member_phone, member_id)VALUES (CONCAT('Member', LPAD(i + 1, 5, '0')), -- 会员姓名,编号后面跟5个0CONCAT('13', LPAD(RAND()*(9999999999-1000000000+1)+1000000000, 10, '0')), -- 随机生成电话号码@random_member_id -- 随机生成的会员编号);-- 增加循环计数器SET i = i + 1;END WHILE;
END //DELIMITER ;

执行存储过程

CALL InsertMember();

验证 limit 查询

执行sql

select * from member order by member_id limit 90000, 10;

limit 分页

可以看到,所用查询时间为 0.227s,相对来说时间偏长了。

子查询优化

先查询出所需要的 10 行数据中的最小 ID 值,然后通过偏移量返回所需要的 10 行数据,可以通过索引覆盖扫描,使用子查询的方式来实现分页查询

SELECT *
FROMmember
WHEREid > (SELECT idFROMmemberORDER BY member_idLIMIT 90000 , 1)
LIMIT 10;

子查询优化
执行时间 0.024s

线上分页

那么在实际的生产环境中,该怎么使用呢?下面我来介绍下我当时是怎么做的。
核心思想就是:分段查询

假如有个订单表,在 【2024-01-01 00:00:00,2024-01-02 00:00:00】有12万条数据, 前 11 个小时段有接近于 1 万条数据,第 12 个小时段有大于 1 万条数据。

现在我们采用分时间段查询,间隔为 1 小时,每次查询 2000 条,那么每个小时段需要查询 5-6次。

先贴出 SQL 代码,方便查看

<select id="grabBizDataSlice" resultMap="BaseResultMap">select<include refid="Base_Column_List"/>from orderwhere update_time &gt;= \#{startTime} and update_time &lt; \#{endTime}and status = 'PROCESS'and id > \#{startRow}order by idlimit  \#{pageSize}</select>

第一个小时

第一次查询

时间段:【2024-01-01 00:00:00,2024-01-01 01:00:00】,
startRow:0
pageSize:2000

第二次查询

时间段:【2024-01-01 00:00:00,2024-01-01 01:00:00】,
startRow:2000
pageSize:2000

第三次查询

时间段:【2024-01-01 00:00:00,2024-01-01 01:00:00】,
startRow: 4000
pageSize:2000

第四次查询

时间段:【2024-01-01 00:00:00,2024-01-01 01:00:00】,
startRow:6000
pageSize:2000

第五次查询

时间段:【2024-01-01 00:00:00,2024-01-01 01:00:00】,
startRow:> 8000
pageSize:2000

注意:第 5 次查询的时候,实际返回的数据量总量已经小于 2000 条了,此时我们就可以判断到第一个小时段的数据已经查询结束了,然后开始第二个时间段的查询,道理是一样的。

redis 存储分段条件

通过上面可以看出来,我们需要有一个地方来保存每次查询的条件的。
这里我是采用的 redis hash 结构。

private final  Map<String, String> bizIdxKeyMap = new HashMap<>();
private final Integer pageSize = 2000;List<B> bizDataList = ...; //从数据库查询的记录
Long pageIdx = bizDataList.size() == pageSize ? bizDataList.get(pageSize - 1).getId() : -1;
bizIdxKeyMap.put("sliceStartCache", sliceStartTime);
bizIdxKeyMap.put("sliceEndCache", sliceEndTime);
bizIdxKeyMap.put("pageIdxCache", pageIdx.toString());
redisCluster.hmset(bizIdxKey, bizIdxKeyMap);

从这里可以看到,当pageIdx = -1时,代表本时间段查询结束了。在下次循环时,再从 redis 中取出来这三个字段 sliceStartCachesliceEndCachepageIdxCache

完整代码

class InitController {@Autowiredprivate BizCommonService bizCommonService;@Autowiredprivate OrderInitServiceImpl orderInitServiceImpl;void calculationFlow(Date startTime, Date endTime) {bizCommonService.initFinanceCalculationCycle(startTime, endTime);orderInitServiceImpl.orderInit();}
}@Service
public class OrderInitServiceImpl{@Autowiredprivate OdsPackOrderDAO odsPackOrderDAO;@Autowiredprivate BizCommonService  bizCommonService;public void orderInit() throws InterruptedException {while(true){String packOrderCalculationSwitch = redisCluster.get("pack_order_switch");if(packOrderCalculationSwitch != null && packOrderCalculationSwitch.equals("switch_off")){break; //查询结束}List<OdsPackOrderDO> odsPackOrderDOList = bizCommonService.grabBizDataSlice(3,TimeUnit.MINUTES, 2000, odsPackOrderDAO, null);// 对查询出来的odsPackOrderDOList做一些业务逻辑}}}@Component
public class BizCommonServicelImpl{@Autowiredprotected RedisCluster redisCluster;private Date financeCycleStartTime;private Date financeCycleEndTime;private final  Map<String, String> bizIdxKeyMap = new HashMap<>();private final static Calendar calendar= Calendar.getInstance();public void initFinanceCalculationCycle(Date startTime, Date endTime) {this.financeCycleStartTime = startTime;this.financeCycleEndTime = endTime;}public List<B> grabBizData(@NonNull Integer interval, TimeUnit intervalUnit, @NonNull Integer pageSize, BD bizDataSource, @Nullable Object customParam){try{String bizIdxKey = "order_index_key"; // 分页条件键String bizSwitchKey = "pack_order_switch"; // 查询终止状态键// 从 redis 查询分页条件键List<String> bizIdxCache = redisCluster.hmget(bizIdxKey, "sliceStartCache", "sliceEndCache", "pageIdxCache");Long pageIdx;Date sliceEndTime;Date sliceStartTime;if(bizIdxCache.get(2) == null ||  bizIdxCache.get(2).equals("-1")){pageIdx = 0L;if(bizIdxCache.get(0) == null){sliceStartTime = financeCycleStartTime;sliceEndTime = timer(sliceStartTime, interval, intervalUnit);}else{sliceStartTime = DateUtils.getDateByMySQLDateTimeString(bizIdxCache.get(1));sliceEndTime = timer(sliceStartTime, interval, intervalUnit);}}else{sliceStartTime = DateUtils.getDateByMySQLDateTimeString(bizIdxCache.get(0));sliceEndTime = DateUtils.getDateByMySQLDateTimeString(bizIdxCache.get(1));pageIdx = Long.valueOf(bizIdxCache.get(2));}// 判断结束标志if(sliceStartTime != null && (sliceStartTime.after(financeCycleEndTime) || sliceStartTime.equals(financeCycleEndTime))){redisCluster.set("pack_order_switch", SWITCH_OFF);return null;}List<B> bizDataList;if(customParam == null) {bizDataList = bizDataSource.grabBizDataSlice(sliceStartTime,sliceEndTime.after(financeCycleEndTime) ? financeCycleEndTime : sliceEndTime,pageIdx,pageSize);}else{bizDataList = bizDataSource.grabBizDataSliceByCustomParam(sliceStartTime,sliceEndTime.after(financeCycleEndTime) ? financeCycleEndTime : sliceEndTime,pageIdx,pageSize,customParam);}pageIdx = bizDataList.size() == pageSize ? bizDataList.get(pageSize - 1).getId() : -1;bizIdxKeyMap.put("sliceStartCache", sliceStartTime);bizIdxKeyMap.put("sliceEndCache", sliceEndTime);bizIdxKeyMap.put("pageIdxCache", pageIdx.toString());redisCluster.hmset("order_index_key", bizIdxKeyMap);return bizDataList;}catch (Exception e){return null;}}private Date timer(Date currentTime, Integer interval, TimeUnit intervalUnit){calendar.setTime(currentTime);if(intervalUnit == TimeUnit.DAYS){calendar.add(Calendar.DATE, interval);}else if(intervalUnit == TimeUnit.HOURS){calendar.add(Calendar.HOUR, interval);}else if(intervalUnit == TimeUnit.MINUTES){calendar.add(Calendar.MINUTE, interval);}else if(intervalUnit == TimeUnit.SECONDS){calendar.add(Calendar.SECOND, interval);}else {throw new RuntimeException("");}return calendar.getTime();}
}

总结

采取合理的分页方式可以有效的提升系统性能,应根据实际情况选择适合自己的方式。
欢迎各位老师分享工作中是怎么使用的,可以交流交流。

相关文章:

高效分页策略:掌握 LIMIT 语句的正确使用方法与最佳实践

本文主要介绍limit 分页的弊端及线上应该怎么用 LIMIT M,N 平时经常见到使用 <limit m,n> 合适的 order by 来实现分页查询&#xff0c;这样做到底性能如何呢&#xff1f; 先来简单分析下&#xff0c;然后再实际验证一下。 无索引条件下&#xff0c;需要做大量的文件排…...

拼图游戏02

文章目录 概要整体架构流程代码过程小结 概要 现在需要将图片添加界面中 关键点在于它如何动态地根据游戏状态更新用户界面。它使用了Swing的布局管理器来定位组件&#xff0c;并且通过ImageIcon和JLabel来显示图像。注意&#xff0c;路径字符串中的反斜杠在Java中是转义字符…...

在本地进行Django支付宝扫码支付-当面付开发

这几天涉及到一个个人项目的支付开发场景&#xff0c;正好完成之后&#xff0c;做一下开发记录&#xff0c;给有需要的朋友做一下参考 涉及安装Python环境请参考我专栏中的历史文章&#xff0c;这里不再重复说明 环境&#xff1a; Python3.11 使用Django框架 因本次代码为沙…...

redis-RedisTemplate.opsForGeo 的geo地理位置相关的方法演示

主要方法&#xff1a;add : 添加一个地理位置distance: 计算两个元素之间的距离hash&#xff1a; 获取元素经纬度坐标经过geohash算法生成的base32编码值position: 获取集合中任意元素的经纬度坐标&#xff0c;可以一次获取多个radius&#xff1a;查询某个坐标或某个成员&#…...

做短视频矩阵要十几人团队吗?云微客助阵,一人即可

现在市面上主流的新媒体平台都进军了短视频赛道&#xff0c;对于众多企业和个人来说&#xff0c;短视频矩阵更是成为了提升影响力和拓展业务的关键。企业或个人可以根据自身产品特点和目标用户群体&#xff0c;构建账号矩阵&#xff0c;在多平台上建立账号矩阵&#xff0c;还可…...

常用语音识别开源工具的对比与实践

常用语音识别开源工具的对比 一.工具概述 1. WeNet 设计目标&#xff1a;WeNet 的设计主要聚焦于端到端&#xff08;E2E&#xff09;语音识别&#xff0c;特别是在流式识别方面的优化。其目标是提供一个可以在实际应用中达到低延迟和高精度的系统。模型架构&#xff1a; Con…...

Fortify代码安全测试工具在静态应用安全测试(SAST)方面针对典型问题的改进

Fortify代码安全测试工具作为行业内资深的老牌软件安全测试工具&#xff0c;可以同时支持静态代码扫描和动态代码扫描&#xff0c;本文我们讲述的主要是在静态代码扫描领域Fortify所面临的问题&#xff0c;以及最新的改进。 在应用安全领域&#xff0c;特别是静态应用安全测试&…...

AWS 消息队列服务 SQS

AWS 消息队列服务 SQS 引言什么是 SQSSQS 访问策略 Access Policy示例&#xff1a;如何为 DataLake Subscription 配置 SQS 引言 应用系统需要处理海量数据&#xff0c;数据发送方和数据消费方是通过什么方式来无缝集成消费数据的&#xff0c;AWS 提供 SQS 消息队列服务来解决…...

【iOS】——响应者链和事件传递链

事件传递 事件传递流程 发生触摸事件后&#xff0c;系统会将该事件封装成UIEvent对象加入到一个由UIApplication管理的事件队列 UIApplication会从事件队列中取出最前面的事件&#xff0c;并将事件分发下去以便处理&#xff0c;通常&#xff0c;先发送事件给应用程序的主窗口…...

mysql查询慢

可能是连接数&#xff0c;或者缓存不够&#xff0c;可尝试添加如下参数&#xff0c;重启mysql [mysqld] max_connections50000 interactive_timeout604800 lock_wait_timeout600 wait_timeout604800 net_read_timeout604800 log-error/var/lib/mysql/mysqld.log slow_qu…...

【Java-==与equals】

与equals区别&#xff1a; 1.是关系运算符&#xff0c;equals()是0bject类中定义的方法 2.基本数据类型: 使用比较值&#xff0c;无法使用equals() 3.引用数据类型: 使用比较内存地址; 如果没有重写equals(),仍然调用的是0bject父类的equals(()方法&#xff0c;则比较的是内…...

ai回答 部署前端项目时需要使用ssh吗

SSH&#xff08;Secure Shell&#xff09;是一种网络协议&#xff0c;用于在不安全的网络上安全地访问远程计算机。SSH 提供了加密的命令行登录到远程计算机的功能&#xff0c;以及远程命令执行和其他服务。它常用于系统管理员管理服务器、开发者进行远程开发、用户通过终端访问…...

结合ChatGPT与Discord,提高团队合作效率

本文将教你如何集成Discord Bot&#xff0c;助力团队在工作中实现更高效的沟通与协作。通过充分发挥ChatGPT的潜力&#xff0c;进一步提升工作效率和团队协作能力。无需编写任何代码即可完成本文所述的操作&#xff0c;进行个性化定制只需对参数进行微调即可。 方案介绍 如果在…...

VisualStudio|开发环境相关技巧及问题

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 本节继续学习VisualStudio相关内容&#xff0c;以前学习都是以能用为主&#xff0c;没有系统的学习&#xff0c;接下来会系统的学习相关内容&#xff0c; 以下为学习笔记。 01 第三方dll调用 ①&#xff1a;如果第三…...

Redis远程字典服务器(11)—— redis客户端介绍

一&#xff0c;基本介绍 前面学习的主要是各种Redis的基本操作/命令&#xff0c;都是再Redis命令行客户端&#xff0c;手动执行的&#xff0c;但是这种方式不是我们日常开发中主要的形式更多的时候&#xff0c;是使用Redis的api&#xff0c;来实现定制化的Redis客户端程序&…...

【mysql】mysql之DDL数据定义语言

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…...

Word文件密码忘记,该如何才能编辑Word文件呢?

Word文件打开之后&#xff0c;发现编辑功能都是灰色的&#xff0c;无法使用&#xff0c;无法编辑&#xff0c;遇到这种情况&#xff0c;是因为Word文件设置了限制编辑导致的。一般情况下&#xff0c;我们只需要输入Word密码&#xff0c;将限制编辑取消就可以正常编辑文件了&…...

解锁移动办公新境界,七款顶尖移动终端管控软件分享!助您轻松掌控每一台移动设备,企业必备!

移动办公&#xff0c;它不仅打破了时间和空间的限制&#xff0c;提高了工作效率&#xff0c;还为员工创造了更加灵活的工作环境。然而&#xff0c;随着移动设备的普及&#xff0c;如何有效管理和控制这些终端&#xff0c;确保信息安全、提升工作效率呢&#xff1f; 今天&#…...

基于微信小程序的大用户心理咨询系统设计与实现---附源码99040

目录 1 绪论 1.1 研究背景 1.2研究现状 1.3论文结构与章节安排 2 基于微信小程序的大用户心理咨询系统设计与实现分析 2.1 可行性分析 2.2 系统功能分析 2.3 系统用例分析 2.4 系统流程分析 2.5本章小结 3 基于微信小程序的大用户心理咨询系统设计与实现总体设计 3.…...

Bigtop 从0开始(上)

本文作者&#xff1a;蔡佳良 原文阅读&#xff1a;【巨人肩膀社区博客分享】Bigtop 从0开始 BigTop的应用场景&#xff1a; 1. BigTop通过提供预配置的Docker镜像&#xff0c;极大简化了在不同操作系统上编译大数据组件的rpm或deb包的过程&#xff0c;使之变得快捷且高效。 …...

Codex入门09-Git工作流(小白入门:不会写commit信息?AI帮你自动生成规范提交)

🎯 本文目标 学会用 Codex 自动化 Git 操作:提交、冲突解决、PR 描述生成。 😰 Git 新手的典型痛点 你的提交记录是不是这样的: git log --oneline a3f4b2c fix 9d1e8c4 update 4c7b91f 修改了一些东西 f0a2d3e 。。。 b5c8e7a 又改了这就是"屎山提交记录"—…...

嵌入式开发实战:从ADC纹波故障看系统集成调试与EMC设计

1. 项目背景与问题缘起&#xff1a;当“新”设备遭遇“老”问题在工业设备开发领域&#xff0c;尤其是像线锯这类集精密机械、复杂电气和嵌入式软件于一体的复杂系统&#xff0c;有一个经典且令人头疼的场景&#xff1a;一款经过验证的成熟产品平台&#xff0c;在衍生出新机型或…...

如何永久保存微信聊天记录:5分钟学会WeChatMsg免费完整指南

如何永久保存微信聊天记录&#xff1a;5分钟学会WeChatMsg免费完整指南 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/…...

Cache映射策略全解析:从全相联到组相连,如何平衡灵活性与效率?

1. 为什么需要Cache映射策略&#xff1f; 想象一下你正在图书馆找一本书。如果每次都要从最外层的书架开始一本本翻找&#xff0c;效率肯定低得令人发指。这时候我们会给书籍分类编号——这就是Cache映射策略的日常类比。 在计算机体系结构中&#xff0c;CPU的运行速度远远快于…...

基于MCP与Apify的ESG供应链风险智能评估工具实战指南

1. 项目概述&#xff1a;一个为AI工作流赋能的ESG供应链风险智能评估工具 如果你是一名ESG分析师、供应链合规官或者投资经理&#xff0c;那么你一定对“供应商ESG尽职调查”这件事又爱又恨。爱的是&#xff0c;它确实能帮你识别潜在的环境、社会和治理风险&#xff0c;避免“…...

AI Agent开发实战:从核心范式到工程落地的完整指南

1. 项目概述&#xff1a;一场静悄悄的技术代际更迭最近和几个技术团队负责人聊天&#xff0c;话题总绕不开“AI Agent”。大家的感觉出奇地一致&#xff1a;这玩意儿的发展速度&#xff0c;快得有点让人喘不过气。新闻里、论文里、各种技术峰会上&#xff0c;关于智能体&#x…...

如何解决QQ音乐下载的歌曲在其他设备上无法播放的问题

如何解决QQ音乐下载的歌曲在其他设备上无法播放的问题 【免费下载链接】qmcflac2mp3 直接将qmcflac文件转换成mp3文件&#xff0c;突破QQ音乐的格式限制 项目地址: https://gitcode.com/gh_mirrors/qm/qmcflac2mp3 你是否曾经在QQ音乐下载了喜欢的歌曲&#xff0c;却发现…...

RuoYi Office 企业多端协同办公落地实战

很多企业在推进数字化办公时&#xff0c;常陷入一个尴尬的境地&#xff1a;PC 端的管理后台功能强大但操作繁琐&#xff0c;移动端的小程序或 App 虽然便捷却数据割裂。HR 在电脑上录入的员工档案&#xff0c;销售在手机里看不到&#xff1b;老板在微信上审批的流程&#xff0c…...

3步终极指南:用开源TCC-G15彻底解决Dell G15散热难题

3步终极指南&#xff1a;用开源TCC-G15彻底解决Dell G15散热难题 【免费下载链接】tcc-g15 Thermal Control Center for Dell G15 - open source alternative to AWCC 项目地址: https://gitcode.com/gh_mirrors/tc/tcc-g15 你是否正在为Dell G15笔记本的过热问题而烦恼…...

内向技术人突破领导力瓶颈:从深度思考到战略沟通的进阶指南

1. 项目概述&#xff1a;内向工程师的“天花板”与破局之路 在技术圈子里待久了&#xff0c;你会发现一个有趣的现象&#xff1a;身边那些能写出精妙算法、搞定复杂架构的工程师&#xff0c;往往在茶水间的闲聊中显得沉默寡言&#xff0c;在大型会议上也更倾向于坐在后排。这并…...