核心是将分页下推至数据库层,避免Java端全量加载;须校验页码与页大小、加索引排序、大数据量时用游标分页替代OFFSET。

Java 中用 JDBC 处理大批量数据的分页查询,核心是避免全表扫描、减少内存占用、防止深分页性能崩塌。不能靠 ResultSet 循环跳过前 N 条(即“游标式分页”),而应交由数据库层完成物理分页,再配合合理参数控制和索引优化。
用数据库原生分页语法,别依赖 ResultSet 跳转
ResultSet 的 absolute() 或 relative() 方法虽能定位行,但底层仍需遍历前面所有记录,数据量一大就卡死,实际开发中基本弃用。正确做法是让 SQL 本身只查目标页数据:
- MySQL:用 LIMIT ? OFFSET ?(如
SELECT * FROM user ORDER BY id LIMIT 20 OFFSET 40) - SQL Server:用 OFFSET ? ROWS FETCH NEXT ? ROWS ONLY
- PostgreSQL:同 MySQL,支持
LIMIT ? OFFSET ? - Oracle:用 ROWNUM 嵌套或 OFFSET … FETCH(12c+)
必须加 ORDER BY,且字段要有索引
没有排序的分页结果不可靠——数据库不保证无序查询的行顺序,翻页时可能重复或漏数据。更重要的是,ORDER BY 字段必须建索引,否则 LIMIT + OFFSET 会变成全表扫描:
- 例如按
create_time DESC分页,就在该字段建 B+ 树索引 - 复合排序如
status, id ASC,建议建联合索引(status, id) - 避免在 ORDER BY 中使用函数或表达式(如
ORDER BY UPPER(name)),会失效索引
慎用 OFFSET,大数据量优先考虑游标分页(Keyset)
当页码很大(比如第 10000 页),OFFSET 200000 会让 MySQL 扫描并丢弃前 20 万行,响应极慢。替代方案是游标分页:
立即学习“Java免费学习笔记(深入)”;
- 不传页码,改传上一页最后一条记录的排序字段值(如
last_id = 12345) - SQL 写成:
SELECT * FROM user WHERE id > ? ORDER BY id LIMIT 20 - 优势:索引直接定位起点,不扫描无关行;劣势:不支持跳页(如直接到第 50 页)
配套做三件事:总数统计、参数校验、连接复用
分页不只是查数据,还要支撑前端显示总页数和防误操作:
-
总数单独查:用
SELECT COUNT(*) FROM user WHERE ...,不要用SELECT COUNT(*) OVER()(MySQL 不支持窗口函数计数) - 校验 pageNum 和 pageSize:pageNum ≤ 1 时默认为 1;pageSize 控制在 10–100 之间,防恶意大页请求
- 用连接池(如 HikariCP),别每次 new Connection;PreparedStatement 预编译 SQL,防注入也提效
不复杂但容易忽略:分页不是写个 LIMIT 就完事,关键在数据库执行计划是否走索引、是否避免回表、是否被 OFFSET 拖垮。先 explain 你的分页 SQL,再上线。


















