## 数据结构 是一个大 List 拆成多个小 List 的问题 ```Java [ 一共 30 个 item,一页 5 个,所以共 6 页,当前在第 3 页 ] ┌───────────────────────────────────────────────────────────────────────┐ │ □ □ □ □ □ | □ □ □ □ □ | ■ ■ ■ ■ ■ | □ □ □ □ □ | □ □ □ □ □ | □ □ □ □ │ └───────────────────────────────────────────────────────────────────────┘ ``` 三个核心参数: 1. 一共有多少数据?totalItems **这是得后端查的** 2. 间隔多少分一次?pageSize **前端用户定的** 3. 现在在哪一段?currentPage **前端用户定的** ## 后端需要做的事情 1. 校验当前页不能小于 1,不能大于最后一页 ```java int curPage = Math.min(params.getCurPage(), (total + params.getPageSize() - 1) / params.getPageSize()); curPage = Math.max(curPage, 1); ``` 1. 把 curPage 和 pageSize 换算成 sql 中的 start 和 offset,这样才能真正减少查询量 2. 一共有多少数据?只有知道总 count,前端才能显示一共有多少页。返回给前端分页信息,前端拿到数据后,可根据 `pagination` 的信息进行分页展示或分页控件渲染。 ```json { "data": [ ... 数据列表 ... ], "pagination": { "totalItems": 30, "pageSize": 5, "currentPage": 3, "totalPages": 6 }, "status": "ok" } ``` ## 常见问题与思考 1. **数据更新导致分页结果不一致** - 在高并发或数据频繁更新场景下,前后两次分页查询之间,数据可能发生变化,造成总数或数据位置变化。常见做法是"最终一致性",即不强求前后端数据一模一样,但保持整体一致即可。 - 如需保证严格一致,可以考虑基于游标(Cursor-Based)或时间戳的分页方式,但实现更为复杂。 2. **大数据量下的性能问题** - 当数据量非常大时,`LIMIT offset, size` 在 MySQL 等数据库中的执行效率可能变差,因为数据库需要扫描和跳过大量数据。 - 可以考虑 **Keyset Pagination(基于主键或索引做"从某个位置往后取")**、**分段聚合** 等方式优化性能。 - 当仅需要查询有限范围的页面时,结合业务规则或用户交互方式进行限制也是一种可行方案。 3. **分页排序** - 大多数应用会使用 `ORDER BY` 进行排序,如果没有合适的索引或排序字段,分页越往后 SQL 的开销会越大,可能会带来较大的数据库压力。需要在需求和数据库设计层面做好索引优化。 4. **缓存与数据库一致性** - 如果使用缓存(如 Redis)来存储 `totalItems` 或数据列表,需要考虑缓存过期策略、更新机制以及是否与数据库实时同步。 - 一般地,如果对实时性要求不高,可以定期刷新缓存减少数据库压力;若对实时性要求高,则需要更精确的增量更新或失效处理。