PageHelper-Cursor:支持游标分页的 PageHelper 修改版,解决 MyBatis 深分页性能问题

PageHelper-Cursor:支持游标分页的 PageHelper 修改版,解决 MyBatis 深分页性能问题

在 MyBatis 项目中,PageHelper是常用的分页插件,它默认采用 LIMIT offset size`的传统物理分页方式 。这种分页很容易在大数据量表中触发深分页性能问题:当 pageNum 越来越大,SQL 执行性能呈指数级下滑,offset 越大扫描代价越高。

举个例子:

sql 复制代码
SELECT * FROM user ORDER BY id LIMIT 1000000, 20;

这种 SQL 将会导致 MySQL 扫描百万级数据后再丢弃数据,拖垮查询性能。

那么有没有更优雅、更高性能的分页方式?

答案是:有!那就是"游标分页"(Cursor Pagination)


PageHelper-Cursor 是什么?

PageHelper-Cursor 是基于 PageHelper 6.1.1 改造的版本,增加了 游标分页能力 ,通过一个有序索引字段充当分页游标 ,绕开了 OFFSET 带来的扫描成本,从而高效支持深分页

github链接,文档齐全:Cursor-PageHelper: Mybatis通用分页插件

部署指南:BUILD_AND_DEPLOY_GUIDE.md

✅ 特性总结

特性 支持
避免深分页性能问题
兼容 PageHelper 使用体验
支持 MySQL & PostgreSQL
支持字段游标分页
使用简单

🚨 注意:该版本目前为非常早期的测试版,性能和可靠性无法保证,强烈建议不要用于生产环境。


为什么游标分页能解决深分页?

游标分页的核心思想:

不再跳过前面所有数据,而是根据上一个查询返回的最后一个字段作为游标继续往后查。

例如:

sql 复制代码
-- 第一次查询 
SELECT * FROM user WHERE id > 0 ORDER BY id LIMIT 20; 
-- 下一次查询,从最后一个 id 开始继续 
SELECT * FROM user WHERE id > 1020 ORDER BY id LIMIT 20;

这种做法依赖索引查找,不会扫全表,性能几乎是常数级的 O(1)


引入 PageHelper-Cursor

如果你的项目是 Spring Boot + MyBatis,在本地部署打包后直接加入下面依赖即可:

xml 复制代码
<!--PageHelper SpringBoot 整合-->
<dependency> 
    <groupId>com.github.pagehelper</groupId> 
    <artifactId>pagehelper-spring-boot-starter</artifactId> 
    <version>${pagehelper-spring-boot-starter.version}</version>
</dependency> 

<!--PageHelper Cursor 测试版--> 
<dependency>
    <groupId>com.github.pagehelper</groupId> 
    <artifactId>pagehelper</artifactId>
    <version>6.1.1-cursor-SNAPSHOT</version> 
</dependency>`

⚠️ 环境要求:最好是MyBatis 3.1.0+


使用示例

✅ 例1:基于自增 ID 分页(最简单)

java 复制代码
@RestController
@RequestMapping("/api/users") 
public class UserController { 
    
    @Autowired private UserMapper userMapper;
    
    @GetMapping public PageInfo<User> getUsers( 
            @RequestParam(required = false, defaultValue = "0") Long cursor) { 
            // 使用游标分页 
            PageHelper.startCursor("id", cursor, 20); 
            List<User> users = userMapper.selectAll(); 
            return new PageInfo<>(users); 
    } 
}

前端分页请求示例:

javascript 复制代码
GET /api/users?cursor=0 GET /api/users?cursor=1001

✅ 例2:使用 create_time 游标分页(更通用)

java 复制代码
@GetMapping("/listWithCursor") 
@Tag(name = "获取所有评论信息", description = "管理员分页获取当前所有评论信息列表") 
public Result<List<TopCommentVo>> queryWithCursor( 
        @RequestParam(required = false) String keyWord, 
        @RequestParam(defaultValue = "0") String lastDate, 
        @RequestParam(defaultValue = "10") int pageSize) { 
    if(lastDate.equals("0")){ 
        lastDate = null; 
    } 
    // 开启分页 
    PageHelper.startCursor("c.create_time",lastDate,pageSize,false); // 执行查询 
    List<TopCommentVo> list = commentService.query(keyWord); 
    // 获取分页信息 
    PageInfo<TopCommentVo> pageInfo = new PageInfo<>(list); 
    // 清理分页 
    PageHelper.clearPage(); 
    // 使用自定义分页返回方法
    return Result.page(list, pageInfo.getTotal()); 
}

注意事项(必看❗)

✅ 成功使用游标分页必须满足几个条件:

条件 必要性
游标字段必须参与排序,且是第一个排序字段 ✅ 必须
游标字段必须有索引支持 ✅ 强烈推荐
暂不支持任意表达式分页 ✅ 不支持
SQL 如有 ORDER BY 必须与游标字段一致 ✅ 必须

与传统分页对比

对比项 LIMIT 分页 Cursor 分页
深分页性能 ❌ 极差 ✅ 稳定
查询效率 O(N) O(1)
依赖 OFFSET ✅ 是 ❌ 否
是否要求索引 ✅ 推荐 ✅ 强制
数据一致性好 ⚠️ 依赖排序字段

总结

PageHelper-Cursor为 MyBatis 提供了高性能分页的新选择,在处理海量分页时性能优势非常明显。它特别适合:

✅ 评论流、订单列表

✅ Feed 流、用户动态

✅ 日志查询、时间排序类数据

目前项目处于测试阶段,如果你对性能敏感、希望替换掉 LIMIT 深分页,相信这个项目会对你有帮助。

相关推荐
hogenlaw7 小时前
自定义注解结合策略模式实现数据脱敏
spring boot
有你有我OK9 小时前
springboot Admin 服务端 客户端配置
spring boot·后端·elasticsearch
xiaoopin11 小时前
简单的分布式锁 SpringBoot Redisson‌
spring boot·分布式·后端
霸道流氓气质18 小时前
SpringBoot+MybatisPlus+自定义注解+切面实现水平数据隔离功能(附代码下载)
java·spring boot·后端
韩立学长18 小时前
【开题答辩实录分享】以《智慧校园勤工俭学信息管理系统的设计与实现》为例进行答辩实录分享
vue.js·spring boot·微信小程序
克莱恩~莫雷蒂19 小时前
Spring Boot 中 controller层注解
java·spring boot·后端
fouryears_234171 天前
Redis缓存更新策略
java·spring boot·redis·spring
ChildrenGreens1 天前
开箱即用的 Web 层解决方案:web-spring-boot-starter 助你统一返回体、异常处理与跨域配置
spring boot
计算机学姐1 天前
基于SpringBoo+Vue的医院预约挂号管理系统【个性化推荐算法+可视化统计】
java·vue.js·spring boot·mysql·intellij-idea·mybatis·推荐算法