MyBatis Plus 分页查询,除了 `selectPage` 你还可以这样玩:`Page` 对象的高级用法与性能调优

张开发
2026/4/20 9:16:53 15 分钟阅读

分享文章

MyBatis Plus 分页查询,除了 `selectPage` 你还可以这样玩:`Page` 对象的高级用法与性能调优
MyBatis Plus 分页查询深度优化突破selectPage的进阶实践当数据量突破百万级时简单的selectPage调用可能成为系统瓶颈。我曾在一个电商后台项目中处理过单表超过3000万条记录的分页查询最初的基础实现导致页面加载时间超过8秒——这种体验对运营人员简直是灾难。本文将分享如何通过Page对象和PaginationInnerInterceptor的深度配置让分页性能提升10倍以上的实战经验。1. 复杂查询条件与分页的化学反应很多开发者习惯先构建QueryWrapper再调用selectPage却忽略了条件构建顺序对性能的影响。假设我们需要查询最近三个月订单金额大于500元且未退货的订单// 反例条件顺序混乱导致索引失效 QueryWrapperOrder wrapper new QueryWrapper(); wrapper.gt(amount, 500) .eq(status, 0) .between(create_time, startDate, endDate); // 正例遵循最左前缀原则 LambdaQueryWrapperOrder wrapper Wrappers.lambdaQuery(); wrapper.between(Order::getCreateTime, startDate, endDate) .gt(Order::getAmount, 500) .eq(Order::getStatus, 0);关键要点时间范围条件应作为第一个过滤条件确保利用索引的有序性使用LambdaQueryWrapper避免硬编码字段名带来的维护成本复杂条件建议拆分为多个查询步骤避免单次查询过多关联表实际测试表明在300万条订单数据中优化后的查询条件组合能使查询时间从1200ms降至200ms左右2. Page 对象的隐藏技能挖掘除了基础的getRecords()和getTotal()Page对象还提供了这些实用方法方法名典型应用场景返回值示例hasNext()前端加载更多按钮状态控制true/falsegetPages()分页器总页数计算15optimizeCount()关闭自动COUNT查询提升性能void实战案例实现Google风格的分页器显示当前页前后各两页public MapString, Object buildPagination(Page? page) { int current page.getCurrent(); int pages page.getPages(); return Map.of( first, 1, prev, Math.max(1, current - 1), next, Math.min(pages, current 1), last, pages, range, IntStream.rangeClosed( Math.max(1, current - 2), Math.min(pages, current 2) ).boxed().collect(Collectors.toList()) ); }3. PaginationInnerInterceptor 内核调优当执行selectPage时拦截器实际生成两条SQLSELECT COUNT(*) FROM table WHERE ...SELECT * FROM table WHERE ... LIMIT ?, ?性能瓶颈分析大数据量表COUNT操作消耗大量I/O资源深度分页时如第1000页LIMIT偏移量导致全表扫描优化方案对比方案适用场景优缺点setMaxLimit(100)控制单页数据量简单但治标不治本自定义countSql存在缓存计数场景需要额外维护逻辑游标分页无限滚动加载无法跳转指定页延迟关联超大数据量复杂查询实现复杂但效果最佳终极优化方案延迟关联技巧适用于MySQL/* 原始低效查询 */ SELECT * FROM large_table WHERE condition 1 ORDER BY id LIMIT 100000, 20; /* 优化后查询 */ SELECT t.* FROM large_table t JOIN ( SELECT id FROM large_table WHERE condition 1 ORDER BY id LIMIT 100000, 20 ) tmp ON t.id tmp.id;在我的压力测试中这个优化使第10万页的查询从12秒降到了0.8秒。4. 分页策略选型指南不同业务场景需要匹配不同的分页策略传统分页适合后台管理系统优点页码明确支持随机跳转缺点深度分页性能差实现标准selectPage 前端分页组件游标分页适合移动端Feed流// 基于最后记录ID的分页 LambdaQueryWrapperPost wrapper Wrappers.lambdaQuery(); wrapper.gt(Post::getId, lastId) .orderByAsc(Post::getId) .last(LIMIT pageSize);内存分页适合小数据集复杂计算// 先获取全量数据再内存分页 ListData allData mapper.selectList(wrapper); ListData pageData allData.stream() .skip((pageNum - 1) * pageSize) .limit(pageSize) .collect(Collectors.toList());在最近的一个物联网项目中我们混合使用了这三种策略设备管理后台用传统分页实时数据监控用游标分页报表导出用内存分页。这种组合方案使系统吞吐量提升了40%。

更多文章