分页查询(Pagination)是数据库开发和 Web 应用中非常常见的功能,用于将大量数据分割成较小的页面,以提高加载速度和用户体验。
以下是关于分页查询的详细介绍,包括原理、常见实现方式、优缺点对比以及最佳实践。
为什么需要分页?
- 性能优化:一次性加载成千上万条数据会消耗大量内存和带宽,导致响应缓慢甚至服务器崩溃。
- 用户体验:用户更容易浏览少量数据,而不是面对一个无限滚动的长列表。
- 资源限制:数据库和网络传输都有带宽和内存限制。
常见的分页方式
✅ 方式一:基于偏移量的分页(Offset Pagination)
这是最传统、最常用的分页方式,使用 LIMIT 和 OFFSET 子句。
SQL 示例(MySQL):
-- 查询第 2 页,每页 10 条数据 SELECT FROM users ORDER BY id ASC LIMIT 10 OFFSET 10;
优点:
- 实现简单,几乎所有数据库都支持。
- 可以直接跳转到任意页(如第 100 页)。
缺点:
- 深分页问题(Deep Pagination):当
OFFSET值很大时(如OFFSET 100000),数据库需要扫描并丢弃前 100,000 条记录,性能急剧下降。
- 数据一致性:如果在分页过程中有新数据插入,可能导致数据重复或遗漏。
✅ 方式二:基于游标的分页(Cursor Pagination / Keyset Pagination)
通过记录上一页最后一条数据的“键值”(如 ID 或时间戳)来查询下一页数据。
SQL 示例(MySQL):
-- 假设上一页最后一条数据的 id 是 100 SELECT FROM users WHERE id > 100 ORDER BY id ASC LIMIT 10;
优点:
- 高性能:无论查询第几页,性能基本一致,因为使用了索引快速定位。
- 数据一致性:不受插入/删除操作影响,不会出现数据重复或遗漏。
缺点:
- 不能直接跳转到任意页(只能“下一页”或“上一页”)。
- 需要业务字段具有唯一性且有序(如自增 ID、创建时间)。
✅ 方式三:基于范围的查询(Range Query)
类似游标分页,但使用范围条件,常用于时间范围或 ID 范围。
SQL 示例:
SELECT FROM orders WHERE created_at > '2026-01-01 00:00:00' ORDER BY created_at ASC LIMIT 10;
分页查询的最佳实践
| 场景 | 推荐分页方式 | 说明 |
|---|---|---|
| 小数据量(< 1000 条) | Offset Pagination | 简单直接,性能影响小 |
| 大数据量 + 需要跳转页码 | Offset Pagination + 优化 | 避免过大的 OFFSET,可考虑限制最大页数 |
| 大数据量 + 只需“下一页” | Cursor Pagination | 性能最优,适合无限滚动列表(如社交媒体) |
| 实时性要求高 | Cursor Pagination | 避免数据重复或遗漏 |
后端代码示例(Java / Spring Boot)
使用 Offset Pagination
public Page<User> getUsers(int page, int size) {
int offset = page size;
List<User> users = userRepository.findAllByOrderByCreatedAtDesc(
PageRequest.of(page, size)
);
long total = userRepository.count();
return new PageImpl<>(users, PageRequest.of(page, size), total);
}
使用 Cursor Pagination
public List<User> getNextUsers(String lastCursorId, int size) {
// lastCursorId 是上一页最后一条数据的 ID
return userRepository.findByIdGreaterThanOrderByCreatedAtDesc(lastCursorId, size);
}
前端展示建议
- 显示页码:适合 Offset 分页,允许用户跳转。
- 无限滚动(Infinite Scroll):适合 Cursor 分页,用户向下滚动自动加载下一页。
- 显示总数:如“共 1000 条,第 1/100 页”,帮助用户了解数据规模。
常见问题与解决方案
| 问题 | 解决方案 |
|---|---|
| OFFSET 很大时查询慢 | 改用 Cursor 分页,或优化索引 |
| 分页数据重复 | 确保 ORDER BY 字段唯一,或使用 Cursor 分页 |
| 总页数计算错误 | 使用 COUNT() 查询总数,但注意 COUNT 在大表上也可能慢,可考虑缓存总数 |
| 高并发下性能瓶颈 | 添加缓存(如 Redis),或使用搜索引擎(如 Elasticsearch)进行分页 |
- 小数据量:用
LIMIT + OFFSET,简单高效。 - 大数据量 + 需要跳转:用
LIMIT + OFFSET,但限制最大偏移量。 - 大数据量 + 无限滚动:用 Cursor 分页,性能最优。
根据你的业务需求选择合适的分页策略,可以显著提升系统性能和用户体验。
首发原创文章,作者:世雄 - 原生数据库架构专家,如若转载,请注明出处:https://idctop.com/article/484327.html



