mysql_page pagesize 如何实现游标分页?

这是一种基于排序字段值 而不是偏移量的分页方法。

核心思想

记住上一页最后一条记录的位置,然后从这个位置开始查询下一页

逐步分解

第一页查询(基础查询)

sql 复制代码
SELECT * FROM loc_common_info 
WHERE id NOT IN (1,2,3)  -- 假设用户去过场地1,2,3
AND status = 'ACTIVE'
ORDER BY popularity_score DESC, id DESC
LIMIT 3;  -- 假设每页3条

假设返回结果:

id name popularity_score
10 场地A 95
8 场地B 95
5 场地C 90

关键点 :我们记录最后一条记录的值:

  • lastScore = 90 (id=5的popularity_score)
  • lastId = 5 (最后一条记录的id)

第二页查询(使用游标)

sql 复制代码
SELECT * FROM loc_common_info 
WHERE id NOT IN (1,2,3) 
AND status = 'ACTIVE'
-- 关键条件:找到排在"最后一条记录"之后的所有记录
AND (popularity_score < #{lastScore} 
     OR (popularity_score = #{lastScore} AND id < #{lastId}))
ORDER BY popularity_score DESC, id DESC
LIMIT 3;

条件逻辑详解

条件分解:

sql 复制代码
AND (
    popularity_score < 90 
    OR 
    (popularity_score = 90 AND id < 5)
)

这个条件的意思是:

  1. popularity_score < 90

    • 找到所有评分严格小于90的记录
    • 这些记录自然排在评分90的记录后面
  2. popularity_score = 90 AND id < 5

    • 找到评分等于 90,但id更小的记录
    • 因为排序是 popularity_score DESC, id DESC,所以id小的排在后面

实际数据示例

假设数据库中有这些数据:

id popularity_score
10 95
8 95
5 90
3 90
7 85
2 85
4 80

第二页查询条件(score < 90) OR (score = 90 AND id < 5)

符合条件的记录:

  • id=3:满足 score = 90 AND id < 5
  • id=7:满足 score < 90
  • id=2:满足 score < 90
  • id=4:满足 score < 90

排序后第二页结果:

id popularity_score
3 90
7 85
2 85

为什么需要复合排序 ORDER BY score DESC, id DESC

如果只按 popularity_score DESC 排序:

问题场景:多个记录有相同的popularity_score

复制代码
第一页:id=10(95), id=8(95), id=5(90)  ← 最后一条id=5, score=90
第二页应该显示:id=3(90), id=7(85), id=2(85)

但如果只按score排序,数据库不保证相同score的记录顺序稳定,可能导致:

  • 第一次查询:id=10(95), id=8(95), id=5(90)
  • 第二次查询:id=10(95), id=8(95), id=3(90) ← 顺序变了!

复合排序确保:

  • 主要按popularity_score降序
  • score相同时,按id降序,保证顺序绝对稳定

前端-后端交互流程

java 复制代码
// 第一页请求
PageRequest request1 = new PageRequest(0, 3, null); // 没有游标
PageResult result1 = service.getRecommendations(request1);

// 返回结果包含下一页游标
String nextCursor = result1.getNextCursor(); // 编码为: "90_5"

// 第二页请求
PageRequest request2 = new PageRequest(0, 3, "90_5"); // 使用游标
PageResult result2 = service.getRecommendations(request2);

与传统OFFSET分页对比

OFFSET分页(有问题):

sql 复制代码
-- 第一页
SELECT ... LIMIT 0, 3;  -- 返回记录1,2,3

-- 第二页  
SELECT ... LIMIT 3, 3;  -- 返回记录4,5,6

问题:如果第一页和第二页之间有新数据插入,会导致:

  • 重复记录(新数据挤占了位置)
  • 丢失记录(原有记录被挤到后面)

游标分页(稳定):

sql 复制代码
-- 第一页:返回id=10,8,5,记住lastScore=90, lastId=5
-- 第二页:查询score<90 OR (score=90 AND id<5)的记录

优势

  • 不受数据增删影响
  • 性能更好(不需要计算OFFSET)
  • 顺序绝对稳定

总结

游标分页的核心就是:记住上一页的终点,从终点开始找下一页,通过复合排序和精确的条件定位,确保分页的准确性和稳定性。

游标对象设计

java 复制代码
@Data
public class RecommendationCursor {
    private String type;        // "new", "rotate", "retain"
    private Double lastScore;   // 上一页最后一条的popularity_score
    private Long lastId;        // 上一页最后一条的ID
    private Integer page;       // 当前页码
    private String randomSeed;  // 随机种子
    
    public static RecommendationCursor parseCursor(String cursorStr, String type) {
        if (cursorStr == null) {
            return new RecommendationCursor(type, null, null, 0, generateRandomSeed());
        }
        // 解析游标逻辑...
    }
    
    public String toCursorString() {
        // 生成游标字符串逻辑...
    }
}

关键要点

  • 不要使用 OFFSET:LIMIT offset, count 在数据量大时性能差且不稳定
  • 游标分页:适合主内容区,保证稳定性和性能
  • 随机分页:适合轮换区,增加多样性
  • 复合排序:ORDER BY score DESC, id DESC 确保排序稳定
  • 状态保持:通过游标或种子保持分页状态

这样设计后,每次分页查询都能返回不同的内容,同时保证性能和稳定性。

相关推荐
泽虞3 小时前
《Qt应用开发》笔记p4
linux·开发语言·数据库·c++·笔记·qt·算法
观远数据4 小时前
A Blueberry 签约观远数据,观远BI以一站式现代化驱动服饰企业新增长
大数据·数据库·人工智能·数据分析
极限实验室4 小时前
Easysearch 冷热架构实战
数据库
不学习何以强国4 小时前
Cool Unix + OpenAuth.Net 实现一款校园小程序的开发
mysql·前端框架·asp.net
Lu Yao_7 小时前
Redis 持久化
数据库·redis·缓存
June`7 小时前
Redis5安装与核心命令详解
数据库·redis·缓存
安当加密10 小时前
达梦数据库TDE透明加密解决方案:构建高安全数据存储体系
网络·数据库·安全
Jabes.yang12 小时前
Java求职面试实战:从Spring Boot到微服务架构的技术探讨
java·数据库·spring boot·微服务·面试·消息队列·互联网大厂
阿巴~阿巴~13 小时前
Redis 核心文件、命令与操作指南
数据库·redis·缓存·客户端·服务端