从 page、page

从 page、page

    正在检查是否收录...

从 page、page_size 到游标:深入解析C端产品的两种主流分页技术

在开发 C 端应用程序时,无论是社交媒体的信息流、电商的商品列表,还是新闻 App 的文章列表,只要涉及到大量数据的展示,“分页”就是一个不可或缺的功能。它不仅能显著提升页面加载速度,还能优化服务器和数据库的性能。

长久以来,page(页码)和 page_size(每页数量)的组合是我们最熟悉的分页方式。然而,随着“无限滚动”和实时数据流的兴起,还有一种叫做“游标分页”的设计。

本文将带你深入了解这两种分页方式的运作原理、优劣势,并结合

Java 实现代码

性能对比

真实案例

,为你介绍这两种技术选型。


一、传统分页:简单直观的 pagepage_size

这是最经典的分页实现,也被称为“偏移量分页”。核心思想是通过指定要跳过的记录数(offset)和要获取的记录数(limit)来查询数据。

工作原理

客户端请求通常包含两个参数:

  • page:当前请求的页码(例如:3)
  • page_size:每页显示的数量(例如:10)

服务器端在收到请求后,会将其转换为数据库查询中的 LIMITOFFSET

SQL 查询示例:

-- 请求第一页 SELECT * FROM items ORDER BY created_at DESC LIMIT 10 OFFSET 0; -- 请求第三页 SELECT * FROM items ORDER BY created_at DESC LIMIT 10 OFFSET 20; 

Java 代码示例

@GetMapping("/items") public PageResponseDTO<Item> list(@RequestParam int page, @RequestParam int pageSize) { Pageable pageable = PageRequest.of(page - 1, pageSize, Sort.by("createdAt").descending()); Page<Item> result = itemRepository.findAll(pageable); return new PageResponseDTO<>( result.getContent(), page, pageSize, result.getTotalElements(), null, null, result.hasNext() ); } 

优点

  1. 实现简单

    :逻辑直观,前后端都容易理解。
  2. 支持跳页

    :用户能直接跳转到指定页码,适合后台管理类系统。

缺点

  1. 深度分页性能差

    OFFSET 会丢弃前面大量数据,1000 页以后性能急剧下降。
  2. 数据不一致

    :数据集频繁更新时,翻页容易出现重复或遗漏。

二、游标设计

游标分页放弃了“页码”的概念,而是用一个“游标”(Cursor)来标记当前位置。常用策略是基于

唯一且有序的字段

(如 (created_at, id))来生成游标。

工作原理

  1. 初始请求

    :客户端请求 /items?limit=10
  2. 服务端响应

    :返回数据 + next_cursor
  3. 后续请求

    :客户端带上游标 /items?limit=10&cursor=xxxx,服务端从游标位置继续取数据。

SQL 查询示例:

-- 初始请求 SELECT * FROM items ORDER BY created_at DESC, id DESC LIMIT 10; -- 假设最后一条记录 created_at='2025-09-05 10:00:00', id=1234 -- 下一页请求 SELECT * FROM items WHERE (created_at < '2025-09-05 10:00:00' OR (created_at = '2025-09-05 10:00:00' AND id < 1234)) ORDER BY created_at DESC, id DESC LIMIT 10; 

Java 实现(游标分页)

@GetMapping("/items/cursor") public PageResponseDTO<Item> cursorPage( @RequestParam(required = false) String cursor, @RequestParam(defaultValue = "10") int limit) { List<Item> items; if (cursor == null) { items = itemRepository.findTopNByOrderByCreatedAtDescIdDesc(limit); } else { CursorPayload cp = cursorCodec.decode(cursor) .orElseThrow(() -> new IllegalArgumentException("Invalid cursor")); items = itemRepository.seekNext(cp.getCreatedAt(), cp.getId(), limit); } String next = items.isEmpty() ? null : cursorCodec.encode(new CursorPayload( items.get(items.size() - 1).getCreatedAt(), items.get(items.size() - 1).getId())); return new PageResponseDTO<>(items, null, null, null, next, null, items.size() == limit); } 

注意这里的 cursorCodec,负责将 (createdAt, id) 编码为

Base64 字符串

,对前端保持不透明。

优点

  1. 性能稳定

    :查询性能与页数无关。
  2. 数据一致性好

    :避免重复和遗漏。
  3. 天然适配无限滚动

    :非常适合信息流。

缺点

  1. 不能跳页

    :用户无法跳转到第 100 页。
  2. 难以统计总数

    :一般只能单独提供 count 接口。
  3. 实现复杂度高

    :需要额外的游标编码、复合索引。

三、关键坑点与解决方案

  1. 时间戳重复导致丢数据

    • (created_at, id) 作为复合游标。
  2. 反向翻页(聊天记录向上加载)

    • 提供 prevCursor,SQL 使用 > 条件,再反转结果。
  3. 游标安全性

    • Base64 + 签名(HMAC)防篡改。
  4. 是否还有更多数据

    • LIMIT = 请求条数 + 1,如果结果超出则说明有更多。

四、性能对比(MySQL)

场景 Offset 分页 游标分页
第 1 页 很快 很快
第 100 页 需要丢弃前 999 条,SQL 变慢 与第一页几乎一致
数据插入 下一页数据错位 不影响
适合场景 后台表格、搜索结果 信息流、聊天、无限滚动

建议实际做 EXPLAIN,偏移量分页深页通常会出现

Using filesort

扫描行数激增

,而游标分页能保持稳定。

  • 本文作者:WAP站长网
  • 本文链接: https://wapzz.net/post-27871.html
  • 版权声明:本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。
本站部分内容来源于网络转载,仅供学习交流使用。如涉及版权问题,请及时联系我们,我们将第一时间处理。
文章很赞!支持一下吧 还没有人为TA充电
为TA充电
还没有人为TA充电
0
0
  • 支付宝打赏
    支付宝扫一扫
  • 微信打赏
    微信扫一扫
感谢支持
文章很赞!支持一下吧
关于作者
2.8W+
9
1
2
WAP站长官方

矩阵的计算和应用

上一篇

教你如何用GPT

下一篇
评论区
内容为空

这一切,似未曾拥有

  • 复制图片
按住ctrl可打开默认菜单