当前位置: 首页 > java >正文

使用LIMIT + OFFSET 分页时,数据重复的风险

在使用 LIMIT + OFFSET 分页时,数据重复的风险不仅与排序字段的唯一性有关,还与数据变动(插入、删除、更新)密切相关。以下是详细分析:

一、数据变动如何导致分页异常

1. 插入新数据

  • 场景:用户在浏览第 1 页时,数据库插入了新记录。
  • 问题:新记录可能会 "挤入" 已浏览过的页面,导致后续页出现重复数据。
  • 示例

    sql

    -- 初始数据(按ID排序)
    ID  Name
    1   Alice
    2   Bob
    3   Charlie-- 第1页:LIMIT 2 OFFSET 0 → 返回 ID=1,2
    -- 此时插入新记录 ID=4
    -- 第2页:LIMIT 2 OFFSET 2 → 返回 ID=3,4(原第2页是ID=3,出现重复)
    

2. 删除数据

  • 场景:用户浏览第 2 页时,第 1 页的某些记录被删除。
  • 问题:第 2 页数据前移,导致部分记录在第 1 页 "消失",第 2 页重复显示。
  • 示例

    sql

    -- 初始数据(按ID排序)
    ID  Name
    1   Alice
    2   Bob
    3   Charlie
    4   Dave-- 第1页:LIMIT 2 OFFSET 0 → 返回 ID=1,2
    -- 此时删除 ID=1
    -- 第2页:LIMIT 2 OFFSET 2 → 返回 ID=3,4(原第2页是ID=3,4,但用户已看过ID=3)
    

3. 更新排序字段

  • 场景:用户浏览第 1 页时,某条记录的排序字段被更新。
  • 问题:记录位置发生变化,导致分页混乱。
  • 示例

    sql

    -- 按分数降序排列
    ID  Score
    1   90
    2   85
    3   80-- 第1页:LIMIT 2 OFFSET 0 → 返回 ID=1,2
    -- 此时 ID=3 的分数更新为 95
    -- 第2页:LIMIT 2 OFFSET 2 → 返回 ID=2,3(ID=2 重复)
    

二、书签 / 键集分页如何避免此问题

书签分页通过记录绝对位置(如id > 100)而非相对偏移量,天然免疫数据变动影响:

  • 插入新数据:新记录不会影响已获取的页,只会出现在第一页。
  • 删除数据:已获取的页不受影响,后续页自动跳过缺失记录。
  • 更新排序字段:若更新影响排序,可能导致数据 "提前" 出现,但不会重复。

三、如何应对数据变动导致的重复问题

1. 业务层规避

  • 场景:社交动态流、实时评论等高频更新场景。
  • 方案
    • 改用书签分页,确保每次查询基于固定位置。
    • 提供 "刷新" 按钮,允许用户重新获取最新数据。

2. 数据库层保障

  • 事务隔离:在高一致性要求场景,使用REPEATABLE READ隔离级别,确保查询期间数据视图不变。
  • 版本控制:为每条记录添加version字段,每次更新时递增,分页时结合版本号排序。

3. 前端处理

  • 去重逻辑:在前端维护已显示的数据 ID 列表,重复数据自动过滤。
  • 无限滚动优化:加载下一页时,保留当前页最后一条数据的 ID,与新页第一条对比。

四、总结:风险场景与应对策略

场景LIMIT+OFFSET 风险书签 / 键集分页风险应对方案
排序字段唯一 + 无数据变动均可使用
排序字段不唯一 + 无数据变动可能重复添加唯一字段到 ORDER BY
排序字段唯一 + 有数据变动可能重复优先使用书签分页
排序字段不唯一 + 有数据变动高风险低风险键集分页 + 唯一字段 + 前端去重

在实际应用中,若数据变动频繁且对一致性要求高,应优先选择书签 / 键集分页,从根本上避免数据重复问题。若使用LIMIT + OFFSET 分页,则需注意,当第一页查询之后(比如我的查询条件每页都是可用量>0,但是第一页查询之后,我在业务代码中将第一页的可用量全部置为0),再去查询下一页,其实会产生跳页情况,因为此时原第一页数据已经不符合查询条件了,真正的第一页会直接被跳过。

http://www.xdnf.cn/news/14841.html

相关文章:

  • Spring Bean 控制销毁顺序的方法总结
  • stm32的三种开发方式
  • js游戏简单修改
  • 【每天一个知识点】子空间聚类(Subspace Clustering)
  • SpringCloud系列(50)--SpringCloud Stream消息驱动之实现消费者
  • Python Async/Await 异步编程详解
  • <script setup>中的setup作用以及和不带的区别对比
  • 【UnityAssetBundle】异步加载
  • 【ESP32-IDF笔记】09-UART配置和使用
  • 基于大模型的领域知识图谱构建--python语言实现
  • Qt处理USB摄像头开发说明与QtMultimedia与V4L2融合应用
  • 二叉树题解——二叉搜索树中第 K 小的元素【LeetCode】使用外部变量ans记录答案
  • MyChrome.exe与Selenium联动避坑指南:User Data目录冲突解决方案
  • 60天python训练营打卡day52
  • Python gmssl.SM4使用案例
  • 动手学深度学习-学习笔记(总)
  • IDEA中application.yml配置文件不自动提示解决办法
  • 运算方法和运算器补充
  • 【AI大模型面试八股文】大模型训练中如何应对灾难性遗忘问题?
  • Swagger 安装使用教程
  • RabbitMQ 4.1.1初体验
  • 一个简单的分布式追踪系统
  • 区块链技术在物联网(IoT)中的核心应用场景
  • 利用TCP协议,创建一个多人聊天室
  • 图灵完备之路(数电学习三分钟)----数据选择器与总线
  • 本地区块链服务在物联网中的应用实例
  • python打卡day58@浙大疏锦行
  • 暴雨服务器成功中标华中科技大学集成电路学院服务器采购项目
  • JAVA-springboot 整合Redis
  • Go中使用国家新闻出版署实名认证