|
MyBatis 本身不提供分页实现,种方它只负责把 SQL 执行结果映射成对象。种方分页这件事,种方得你自己在 SQL 层面或者插件层面解决。种方我见过太多项目在分页上踩坑,种方尤其是种方数据量上了百万级之后,内存分页直接把服务拖垮。种方 最原始的种方方式是内存分页,也就是种方把全量数据查出来,再用 这种代码在测试环境跑得挺欢,生产环境数据一多就炸。种方假设一张表 50 万行,种方每页 20 条,种方用户翻到第 10 页,种方你查了 50 万条数据进 JVM,再丢掉 49.98 万条。GC 压力、网络传输、数据库 IO 全是浪费。除非你确定这张表永远不超过几千行,否则别用。 SQL 层面分页:LIMIT 和 OFFSETMySQL 里最常见的写法是 Java 侧传参时算好 offset。这种方式简单直接,适合中小数据量。但 OFFSET 有个硬伤:翻到后面页码时,数据库要先扫描并丢弃前面所有行。比如 我实测过一张 300 万行的订单表, 优化思路是用游标分页,也就是基于上一页最后一条记录的 ID 来查下一页: 这种方式没有 OFFSET 的扫描开销,每次查询都走索引。缺点是不能随机跳页,只能上一页下一页。适合信息流、滚动加载场景。后台管理系统那种要跳转到第 50 页的需求,游标分页就无能为力了。 PageHelper 插件:省事但要知道它干了什么PageHelper 是国内用得最多的 MyBatis 分页插件。代码写起来很舒服: 它通过 MyBatis 拦截器在 SQL 执行前改写语句,自动加上 LIMIT,同时执行一条 COUNT 查询。你不需要手动写分页 SQL,但代价是 COUNT 查询可能很慢。PageHelper 默认会生成 我遇到过一种情况:原查询带 GROUP BY,PageHelper 的 COUNT 结果和实际总行数对不上。后来查文档才发现, 另外 PageHelper 对多数据源的支持需要额外配置, 手写 COUNT 和分页 SQL:可控性最强对性能敏感的项目,我倾向于手写两条 SQL。一条查总数,一条查当前页数据。COUNT 语句可以针对业务做优化,比如去掉不必要的 JOIN,或者用近似值代替精确值。 Service 层组装分页对象,返回给前端。这种方式代码量多一些,但每一行 SQL 都在你掌控之中。出了慢查询,直接看 SQL 就能定位问题,不用去猜插件生成了什么。 还有个细节:ORDER BY 的字段一定要有索引,否则 LIMIT 也救不了你。我见过 分页方案没有银弹。数据量小就内存分页图省事,中等数据量用 LIMIT 加索引,大数据量考虑游标分页或者把 COUNT 缓存起来。PageHelper 适合快速开发,但别把它当成黑盒,至少要知道它替你执行了哪几条 SQL。 |
