分页优化之——游标分页

游标分页(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

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

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

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

相关推荐
我星期八休息16 分钟前
C++智能指针全面解析:原理、使用场景与最佳实践
java·大数据·开发语言·jvm·c++·人工智能·python
摇滚侠16 分钟前
Spring Boot 3零基础教程,WEB 开发 整合 Thymeleaf 笔记36
java·spring boot·笔记
大猫会长21 分钟前
docker安装php+apache
java·开发语言
野生技术架构师23 分钟前
JAVA 架构师面试题含答案:JVM+spring+ 分布式 + 并发编程
java·jvm·spring
瑞士卷@35 分钟前
MyBatis入门到精通(Mybatis学习笔记)
java·数据库·后端·mybatis
梵得儿SHI1 小时前
Java 反射机制深度剖析:性能与安全性的那些坑
java·开发语言·安全·反射·动态代理·性能·反射机制
虫小宝1 小时前
Java分布式架构下的电商返利APP技术选型与架构设计实践
java·分布式·架构
007php0071 小时前
百度面试题解析:Zookeeper、ArrayList、生产者消费者模型及多线程(二)
java·分布式·zookeeper·云原生·职场和发展·eureka·java-zookeeper
4Forsee1 小时前
【Android】浅析 Android 的 IPC 跨进程通信机制
android·java
来旺2 小时前
互联网大厂Java面试全解析及三轮问答专项
java·数据库·spring boot·安全·缓存·微服务·面试