游标分页实现

游标分页(Cursor-based Pagination)是一种针对大数据量分页查询的高效解决方案,尤其适用于‌避免深分页性能问题‌的场景(如千万级数据分页)。以下是其核心原理、实现方式及优化细节:

一、传统分页的问题

传统分页使用 LIMIT offset, size(如 LIMIT 10000, 50)时:

  1. 性能瓶颈‌:MySQL 需要先扫描前 offset + size 条数据,再丢弃前 offset 条,效率极低。
  2. 数据漂移‌:若数据频繁增删,OFFSET 分页会导致重复或遗漏(如第2页查询时,第1页数据已变化)。

二、游标分页的核心思想

‌基于有序游标‌:使用 ‌唯一且有序的字段‌(如自增主键、时间戳)作为分页锚点。

‌定位式查询‌:每次查询基于上一页的最后一个值,直接跳过已读数据。

三、实现步骤

‌1. 确定游标字段‌

‌理想字段‌:自增主键(id)、时间戳(created_at)等。
‌要求‌:字段唯一、有序,且数据按此字段排序。

‌2. 分页查询SQL

-- 第一页(初始查询)
SELECT id, phone, name 
FROM students 
ORDER BY id ASC 
LIMIT 50;

-- 后续页(基于上一页的最后一个id)
SELECT id, phone, name 
FROM students 
WHERE id > {last_id}  -- 锚点定位
ORDER BY id ASC 
LIMIT 50;

  1. 分页参数传递
    客户端‌:记录当前页的最后一个 id(即游标),将其作为下一页的查询条件。
    ‌服务端‌:无需计算总页数,只需返回数据及是否有下一页的标记。

四:代码示例

使用mybastis

  1. 定义 Mapper 接口
SELECT id, name, phone FROM students WHERE id > ? ORDER BY id LIMIT 50;

  1. 编写 XML 映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.StudentMapper">

    <select id="selectStudentsByCursor" resultType="com.example.model.Student">
        SELECT id, name, phone
        FROM students
        <if test="lastId != null and lastId != ''">
            WHERE id > #{lastId}
        </if>
        ORDER BY id
        LIMIT 50
    </select>

</mapper>


  1. 在 Service 层调用 Mapper 方法
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class StudentService {

    @Autowired
    private StudentMapper studentMapper;

    public void processStudents() {
        String lastId = null; // 初始值为null,表示从头开始

        while (true) {
            List<Student> students = studentMapper.selectStudentsByCursor(lastId);
            if (students.isEmpty()) {
                break;
            }

            for (Student student : students) {
                // 处理当前批次的数据,例如发送到Kafka
                System.out.println("Processing student: " + student);
            }

            // 更新lastId为当前批次最后一个学生的id
            lastId = students.get(students.size() - 1).getId();
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@淡 定

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值