举例说明MyBatis中逻辑分页的优缺点
MyBatis 中逻辑分页的优缺点及示例说明
一、逻辑分页(RowBounds)的核心原理
逻辑分页是指 MyBatis 先从数据库查询所有数据到内存,再通过RowBounds
对象对结果集进行内存截取,实现 "分页" 效果。其本质是内存分页,而非数据库层面的分页。
二、逻辑分页的优点:简单兼容
1. 代码实现极简
无需修改 SQL 语句,只需在调用时传入RowBounds
参数,适合快速开发。
// Mapper接口(普通查询)
List<User> selectAllUsers();// 调用时传入RowBounds实现分页
int pageNum = 2; // 页码
int pageSize = 10; // 每页条数
int offset = (pageNum - 1) * pageSize;RowBounds rowBounds = new RowBounds(offset, pageSize);
List<User> users = userMapper.selectAllUsers(rowBounds);
2. 跨数据库兼容性强
不依赖数据库特定的分页语法(如 MySQL 的LIMIT
、Oracle 的ROWNUM
),一套代码适配多种数据库。
3. 适合小数据量场景
当数据总量较小(如几百条)时,内存开销可忽略,逻辑分页的便捷性优势明显。
4. 灵活的分页参数控制
可在运行时动态调整offset
和pageSize
,无需修改 SQL 或接口。
三、逻辑分页的缺点:性能与内存瓶颈
1. 大数据量下内存溢出风险
- 场景示例:假设数据库有 10 万条记录,每页 10 条,查询第 10000 页时:
// 错误用法:查询全量数据再分页
int offset = 99990; // (10000-1)*10
RowBounds rowBounds = new RowBounds(offset, 10);
List<User> users = userMapper.selectAllUsers(rowBounds); // 实际查询了10万条数据到内存
- 问题:数据库返回全量数据到内存,可能导致
OutOfMemoryError
。
2. 查询性能差
数据库需执行全量查询,网络传输和内存处理开销大,尤其在数据量超过百万级时,查询响应时间显著变长。
3. 无法利用数据库分页优化
- 数据库无法针对分页条件优化索引(如 MySQL 的
LIMIT
可利用覆盖索引)。 - 无法使用数据库特有的分页性能优化方案(如 Oracle 的
ROWNUM
前缀优化)。
4. 数据一致性问题
查询期间若数据库数据变更,内存中的全量数据可能与数据库不一致,导致分页结果不准确。
四、典型应用场景与避坑建议
1. 适用场景
- 管理后台小数据量列表(如数据总量 < 1000 条)。
- 临时查询工具或调试场景。
- 兼容多种数据库的快速原型开发。
2. 避坑方案
- 限制数据总量:在 SQL 中添加
LIMIT
上限(如SELECT * FROM table LIMIT 10000
),避免全量查询
<!-- XML中添加总量限制 -->
<select id="selectAllUsers" resultType="User">SELECT * FROM users LIMIT 10000
</select>
- 配合物理分页降级:当检测到页码过大时,自动切换为物理分页(如使用 PageHelper)。
五、与物理分页的核心对比
维度 | 逻辑分页(RowBounds) | 物理分页(数据库原生 / LIMIT) |
---|---|---|
数据加载 | 全量加载到内存后截取 | 数据库直接返回分页结果 |
内存占用 | 高(全量数据) | 低(仅返回当前页数据) |
数据库压力 | 低(SQL 简单) | 中(需数据库处理分页) |
性能上限 | 差(受内存限制) | 好(数据库优化支持) |
适用场景 | 小数据量、快速开发 | 大数据量、生产环境 |
总结
逻辑分页的简单性和兼容性使其适合轻量级场景,但在生产环境中,尤其是数据量较大时,应优先选择物理分页(如 PageHelper 插件或数据库原生分页)。实际开发中可根据数据规模灵活选择:小数据量用逻辑分页,大数据量用物理分页,同时通过代码限制防止全量数据加载导致的性能风险。