分页优化之——游标分页

游标分页(Cursor-based Pagination) 是一种高效的分页方式,特别适用于大数据集和无限滚动的场景。与传统的基于页码的分页(如 page=1&size=10)不同,游标分页通过一个唯一的游标(通常是时间戳或唯一 ID)来标记分页的位置,避免了传统分页在数据变动时的重复或遗漏问题。

以下是游标分页在前后端的实现方式:


1. 游标分页的核心概念

  1. 游标(Cursor)

    • 游标是一个唯一标识符,通常是数据的某个字段(如 idcreated_at)。

    • 游标用于标记分页的起始位置。

  2. 分页方向

    • 向前分页(Next Page):获取游标之后的记录。

    • 向后分页(Previous Page):获取游标之前的记录。

  3. 响应结构

    • 返回分页数据时,需要包含下一个游标和上一个游标,以便客户端继续分页。

2. 后端实现

2.1 数据库查询

假设数据表结构如下:

复制代码
CREATE TABLE posts (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    content TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
  • 向前分页 :获取 id > cursor 的记录。

  • 向后分页 :获取 id < cursor 的记录。

2.2 后端 API 设计
  • 请求参数

    • cursor:当前游标(可选,首次请求时可以为空)。

    • limit:每页的记录数。

    • direction:分页方向(nextprev,可选)。

  • 响应结构

    复制代码
    {
        "data": [],         // 当前页的数据
        "next_cursor": "123", // 下一页的游标
        "prev_cursor": "456"  // 上一页的游标
    }
2.3 后端代码实现(Java + Spring Boot)
java 复制代码
@RestController
@RequestMapping("/posts")
public class PostController {

    @Autowired
    private PostRepository postRepository;

    @GetMapping
    public ResponseEntity<CursorPageResponse<Post>> getPosts(
            @RequestParam(required = false) Long cursor,
            @RequestParam(defaultValue = "10") int limit,
            @RequestParam(defaultValue = "next") String direction) {

        List<Post> posts;
        Long nextCursor = null;
        Long prevCursor = null;

        if ("next".equals(direction)) {
            // 向前分页:获取 id > cursor 的记录
            posts = postRepository.findByIdGreaterThan(cursor, PageRequest.of(0, limit));
            if (!posts.isEmpty()) {
                nextCursor = posts.get(posts.size() - 1).getId();
                prevCursor = posts.get(0).getId();
            }
        } else if ("prev".equals(direction)) {
            // 向后分页:获取 id < cursor 的记录
            posts = postRepository.findByIdLessThan(cursor, PageRequest.of(0, limit));
            if (!posts.isEmpty()) {
                nextCursor = posts.get(0).getId();
                prevCursor = posts.get(posts.size() - 1).getId();
            }
        } else {
            // 首次请求,获取最新的记录
            posts = postRepository.findLatest(PageRequest.of(0, limit));
            if (!posts.isEmpty()) {
                nextCursor = posts.get(posts.size() - 1).getId();
            }
        }

        CursorPageResponse<Post> response = new CursorPageResponse<>();
        response.setData(posts);
        response.setNextCursor(nextCursor);
        response.setPrevCursor(prevCursor);

        return ResponseEntity.ok(response);
    }
}

3. 前端实现

3.1 首次加载
  • 首次加载时,不传递 cursor,获取最新的数据。
javascript 复制代码
fetch('/posts?limit=10')
    .then(response => response.json())
    .then(data => {
        console.log(data);
        // 渲染数据
        // 保存 next_cursor 和 prev_cursor
    });
3.2 加载下一页
  • 使用 next_cursor 请求下一页数据。
javascript 复制代码
fetch(`/posts?cursor=${nextCursor}&limit=10&direction=next`)
    .then(response => response.json())
    .then(data => {
        console.log(data);
        // 渲染数据
        // 更新 next_cursor 和 prev_cursor
    });
3.3 加载上一页
  • 使用 prev_cursor 请求上一页数据。
javascript 复制代码
fetch(`/posts?cursor=${prevCursor}&limit=10&direction=prev`)
    .then(response => response.json())
    .then(data => {
        console.log(data);
        // 渲染数据
        // 更新 next_cursor 和 prev_cursor
    });

4. 游标分页的优点

  1. 高效

    • 基于游标的分页可以利用索引,查询性能更高。
  2. 稳定性

    • 数据变动时(如新增或删除记录),游标分页不会出现重复或遗漏问题。
  3. 适合无限滚动

    • 无限滚动场景下,游标分页比传统分页更自然。

5. 游标分页的缺点

  1. 不支持随机跳页

    • 游标分页只能按顺序加载下一页或上一页,无法直接跳转到指定页码。
  2. 实现复杂度较高

    • 需要前后端协同设计游标逻辑。

6. 总结

  • 游标分页 是一种高效且稳定的分页方式,特别适合大数据集和无限滚动场景。

  • 后端通过游标(如 idcreated_at)实现分页查询,并返回 next_cursorprev_cursor

  • 前端根据游标加载下一页或上一页数据。

  • 与传统分页相比,游标分页更适合动态数据场景,但无法支持随机跳页。

通过以上实现,可以高效地处理大数据集的分页需求,同时避免传统分页的常见问题。

相关推荐
惊讶的猫5 小时前
探究StringBuilder和StringBuffer的线程安全问题
java·开发语言
jmxwzy5 小时前
Spring全家桶
java·spring·rpc
Halo_tjn5 小时前
基于封装的专项 知识点
java·前端·python·算法
Fleshy数模6 小时前
从数据获取到突破限制:Python爬虫进阶实战全攻略
java·开发语言
像少年啦飞驰点、6 小时前
零基础入门 Spring Boot:从“Hello World”到可上线的 Web 应用全闭环指南
java·spring boot·web开发·编程入门·后端开发
苍煜6 小时前
万字详解Maven打包策略:从基础插件到多模块实战
java·maven
有来技术6 小时前
Spring Boot 4 + Vue3 企业级多租户 SaaS:从共享 Schema 架构到商业化套餐设计
java·vue.js·spring boot·后端
东东5167 小时前
xxx医患档案管理系统
java·spring boot·vue·毕业设计·智慧城市
一个响当当的名号8 小时前
lectrue9 索引并发控制
java·开发语言·数据库
进阶小白猿8 小时前
Java技术八股学习Day30
java·开发语言·学习