Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > mysql游标分页

mysql实现游标分页的方法详解

作者:惊鸿一博

这篇文章主要为大家详细介绍了mysql实现游标分页的相关方法,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下

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

核心思想

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

逐步分解

第一页查询(基础查询)

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条

假设返回结果:

idnamepopularity_score
10场地A95
8场地B95
5场地C90

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

第二页查询(使用游标)

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;

条件逻辑详解

条件分解

AND (
    popularity_score < 90 
    OR 
    (popularity_score = 90 AND id < 5)
)

这个条件的意思是:

popularity_score < 90

popularity_score = 90 AND id < 5

实际数据示例

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

idpopularity_score
1095
895
590
390
785
285
480

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

符合条件的记录:

排序后第二页结果:

idpopularity_score
390
785
285

为什么需要复合排序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的记录顺序稳定,可能导致:

复合排序确保:

前端-后端交互流程

// 第一页请求
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分页(有问题)

-- 第一页
SELECT ... LIMIT 0, 3;  -- 返回记录1,2,3

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

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

游标分页(稳定)

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

优势

总结

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

游标对象设计

@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() {
        // 生成游标字符串逻辑...
    }
}

关键要点

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

到此这篇关于mysql实现游标分页的方法详解的文章就介绍到这了,更多相关mysql游标分页内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文