微信聊天记录永久保存:免费开源工具WeChatMsg完整使用指南
2026/5/31 17:05:27
| 特性 | 传统分页 | 游标分页 |
|---|---|---|
| 定义 | 使用 OFFSET 和 LIMIT 参数,通过跳过前面的记录来获取指定页的数据。 | 使用一个游标(cursor)来标记当前位置,基于游标位置获取后续数据。 |
| 特点 | 1. 需要知道总记录数(COUNT查询) 2. 使用页码(page)和每页数量(size) 3. 可以跳转到任意页面 | 1. 不需要总记录数 2. 使用游标(cursor)和每页数量(size) 3.只能顺序翻页,不能跳转 |
| 优点 | 1. 可以跳转页面:用户可以直接跳转到第N页 2. 显示总数:可以显示总记录数和总页数 3. 实现简单:逻辑直观,易于理解 4. 兼容性好:所有数据库都支持 OFFSET和LIMIT | 1.性能优秀: + 不需要 COUNT(*)查询+ 查询速度稳定,不受数据量影响 + 使用索引高效定位 2.数据一致性: + 基于游标位置查询,不受数据变化影响 + 不会出现重复或遗漏数据 3.资源消耗低: + 不需要统计总数 + 查询效率高 |
| 缺点 | 1.性能问题: + COUNT(*)查询在大数据量下很慢+ OFFSET越大,查询越慢(需要跳过更多记录)2.数据一致性问题: + 在翻页过程中,如果有数据新增或删除,可能导致: - 重复数据(同一数据出现在两页) - 遗漏数据(某些数据永远不会被看到) | 1. 不能跳转页面:只能顺序翻页,不能直接跳转到第N页 2. 显示总数:无法显示总记录数和总页数 3. 实现复杂:需要处理游标编码/解码 4. 游标管理:需要确保游标的唯一性和稳定性 |
| 应用场景 | 1.需要显示总数和总页数 + 商品列表需要显示"共1000件商品" + 订单列表需要显示"共50页" 2.需要跳转页面 + 用户可以输入页码跳转 + 需要显示页码导航(1, 2, 3…) 3.数据量不大 + 数据量在10万以内 + 查询频率不高 4.管理后台 + 管理员需要查看总数 + 需要跳转到指定页面 | 1.大数据量场景 + 数据量超过10万条 + 需要高性能查询 2.移动端列表 + 无限滚动加载 + 不需要显示总数 3.实时性要求高 + 数据频繁变化 + 需要保证数据一致性 4.C端应用 + 用户主要浏览最新数据 + 不需要跳转到历史页面 5.时间线/动态流 + 微博、朋友圈等时间线 + 订单列表(按时间排序) |
-- 先查询总数SELECTCOUNT(*)FROMtrade_orderWHEREuser_id='xxx';-- 第一页(page=1, size=10)SELECT*FROMtrade_orderWHEREuser_id='xxx'ORDERBYcreate_timeDESCLIMIT10OFFSET0;-- 第二页(page=2, size=10)SELECT*FROMtrade_orderWHEREuser_id='xxx'ORDERBYcreate_timeDESCLIMIT10OFFSET10;执行流程:
COUNT(*)查询获取总记录数OFFSET = (page - 1) * sizeOFFSET条记录-- 第一页(无游标)SELECT*FROMtrade_orderWHEREuser_id='xxx'ORDERBYcreate_timeDESC,idDESCLIMIT11;-- 查询11条,用于判断是否有更多数据-- 第二页(使用游标)SELECT*FROMtrade_orderWHEREuser_id='xxx'AND(create_time<'2025-12-16 10:00:00'OR(create_time='2025-12-16 10:00:00'ANDid<'xxx-uuid'))ORDERBYcreate_timeDESC,idDESCLIMIT11;执行流程:
createTime和idcreate_time < cursor.createTime OR (create_time = cursor.createTime AND id < cursor.id)size + 1条数据(多查1条用于判断是否有更多数据)size + 1条,说明还有更多数据,返回前size条并生成下一个游标size条,说明没有更多数据说明:
COUNT(*)查询时间随数据量线性增长OFFSET越大,查询越慢| 操作 | 传统分页 | 游标分页 |
|---|---|---|
| 每次查询SQL数量 | 2条(COUNT + SELECT) | 1条(SELECT) |
| COUNT查询 | 需要全表扫描或索引扫描 | 不需要 |
| OFFSET操作 | 需要跳过N条记录 | 不需要 |
| 索引利用 | 部分利用 | 完全利用 |
POST /api-portal/trade/order/page { "page": 2, "size": 10, "status": 10, "createTime": ["2025-11-16 00:00:00", "2025-12-16 00:00:00"] }{ "code": 0, "data": { "list": [...], "total": 1000, "page": 2, "size": 10, "pages": 100 } }-- 1. 查询总数 SELECT COUNT(*) FROM trade_order WHERE user_id = 'xxx' AND status = 10 AND create_time BETWEEN '2025-11-16' AND '2025-12-16'; -- 2. 查询数据 SELECT * FROM trade_order WHERE user_id = 'xxx' AND status = 10 AND create_time BETWEEN '2025-11-16' AND '2025-12-16' ORDER BY create_time DESC LIMIT 10 OFFSET 10;POST /api-portal/trade/order/cursor-page { "size": 10, "status": 10, "createTime": ["2025-11-16 00:00:00", "2025-12-16 00:00:00"] }{ "code": 0, "data": { "list": [...], "nextCursor": "MjAyNS0xMi0xNlQxMDowMDowMHw2ZTdhNTVlYi0zMzc0LTRjMDYtYmEzZi1mZGUwMmU5MGU5MWU=", "hasMore": true } }POST /api-portal/trade/order/cursor-page { "cursor": "MjAyNS0xMi0xNlQxMDowMDowMHw2ZTdhNTVlYi0zMzc0LTRjMDYtYmEzZi1mZGUwMmU5MGU5MWU=", "size": 10, "status": 10, "createTime": ["2025-11-16 00:00:00", "2025-12-16 00:00:00"] }-- 查询 size + 1 条数据 SELECT * FROM trade_order WHERE user_id = 'xxx' AND status = 10 AND create_time BETWEEN '2025-11-16' AND '2025-12-16' AND (create_time < '2025-12-16 10:00:00' OR (create_time = '2025-12-16 10:00:00' AND id < 'xxx-uuid')) ORDER BY create_time DESC, id DESC LIMIT 11;⚠️需要注意的问题:
createTime + id组合)id)createTime单独排序)DISTINCT去重✅推荐做法:
// 使用 createTime|id 格式,Base64编码 cursor = Base64.encode("2025-12-16T10:00:00|uuid-string")ORDER BY create_time DESC, id DESC -- 确保排序的唯一性和稳定性WHERE (create_time < cursor.createTime OR (create_time = cursor.createTime AND id < cursor.id))// 查询 size + 1 条 List<Order> orders = query(size + 1); boolean hasMore = orders.size() > size; if (hasMore) { orders = orders.subList(0, size); nextCursor = createCursor(orders.get(size - 1)); }| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 移动端列表(无限滚动) | 游标分页 | 性能好,数据一致 |
| 管理后台(需要总数) | 传统分页 | 需要显示总数和跳转 |
| 大数据量(>10万) | 游标分页 | 性能优势明显 |
| 小数据量(<10万) | 传统分页 | 实现简单 |
| 实时数据流 | 游标分页 | 数据一致性好 |
| 需要跳转页面 | 传统分页 | 游标分页不支持 |