806 字
约 2 分钟
7
S08-J01 · 分页:PageHelper vs MP 分页插件

适用读者:写过 limit 但没想过"插件怎么改 SQL"的人。
前置:S08-B05。
实测环境:MyBatis-Plus 3.5.7 / H2(2026-11-05)。
覆盖旧文:904(85) 1179(80) 1180(58) + 空壳 1145 补写 = 223 点。
覆盖扩展:E-S08-004(limit 深翻慢)、E-S08-011(空壳 1145 分页,已补写)。
上一篇:S08-B05 · Wrapper 条件构造器
下一篇:S08-J02 · 插件链。


1. 一句话结论

分页插件不是"帮你查数据",是"拦截你的 SQL,自动加 LIMIT,并额外发一条 COUNT"。

实测(MP 分页 page=1,size=1):

==> Preparing: SELECT COUNT(*) AS total FROM sys_user WHERE deleted = 0
==> Preparing: SELECT id,user_name,age,deleted,version FROM sys_user WHERE deleted=0 ORDER BY id ASC LIMIT ?
6. 分页 page=1,size=1 → 记录数 1, total=2, pages=2

两条 SQL(count + list),LIMIT ? 是参数化的。


2. 两种实现

方案 机制 特点
PageHelper(MyBatis) 拦截 Executor.query,改写 SQL 加 limit 需 startPage(),有"线程变量"风险
MP 分页插件 PaginationInnerInterceptor 与 Page 对象天然结合

PageHelper 的坑:PageHelper.startPage() 是基于 ThreadLocal 的——如果后面紧跟的不是分页查询(或异步执行),会"分页错位"。MP 的 Page 参数化方式更安全。


3. LIMIT 深翻为什么慢(E-S08-004)

select * from t order by id limit 1000000, 20;   -- 要扫 100 万行再丢弃
方案 做法 代价
深翻 limit offset, size offset 越大越慢
游标分页(seek) where id > lastId order by id limit 20 ✅ O(1) 定位
延迟关联 join (select id ... limit ...) 减少回表

判据:"用户会翻到第 100 页吗?" 会 → 用游标分页;不会 → 普通 limit 够用。

生产上的分页几乎都是"前几页"——为深翻做的优化常是过度设计,但要有兜底(如游标)。


4. 分页的四个注意点

点 说明
count 也要走索引 count 查询慢会拖累整个分页
排序字段必须有索引 否则 order by 全表排
total 可关闭 不需要总数时(如移动端下拉)关掉省一次查询
分页大小上限 防止 size=100000 拖垮数据库

5. 五个误区

误区 真相
"分页插件只发一条 SQL" 通常两条(count + list)
"offset 大只是慢一点" 是线性变慢
"count 一定快" 复杂 where 下可能比 list 还慢
"PageHelper 全局无副作用" ThreadLocal 有错位风险
"分页大小随便传" 需服务端限流上限

6. 自检

  1. 分页插件实际发几条 SQL?各是什么?
  2. PageHelper 的 ThreadLocal 风险是什么?
  3. 深翻为什么慢?三种优化方案?
  4. count 查询可能成为瓶颈吗?
  5. 为什么分页大小必须限制上限?

7. 下一篇

S08-J02 · 插件与拦截器链

分页讲完,下一篇讲"插件到底能拦哪四处":Executor/StatementHandler/ParameterHandler/ResultSetHandler。


本篇属于系列《S08 · MyBatis 持久层》。旧文来源:A-904/1179/1180(空壳 1145 补写);扩展点 E-S08-004。count+limit 两条 SQL 为 MP 3.5.7 实测。

S08-J01 · 分页:PageHelper vs MP 分页插件
https://www.clxhxhhr.top/posts/5287/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。