最近在开发一个数据报表功能,需要从一张大表中查询数据,经过二次清洗后写入报表库。清洗过程中需要调用第三方服务,因此无法使用传统的游标查询方案——因为游标查询通常需要在事务中执行,而事务中调用外部服务可能导致事务超时,进而引发数据库异常。
基于以上限制,我们转向分页查询方案,即分批查询数据、分批清洗写入。下面将介绍几种常用的 MySQL 分页方案,并对其适用场景、优缺点进行分析。
一、LIMIT OFFSET 分页方案
这是最常见的前端分页实现方式,语法简单、易于理解。
基本语法
SELECT * FROM table_name LIMIT 20 OFFSET 1000;
-- 或等价写法:
SELECT * FROM table_name LIMIT 1000, 20;
优点
实现简单,符合直觉。
支持随机跳页,适用于用户交互式分页。
缺点
随着 OFFSET 增大,数据库需要扫描并跳过前 OFFSET 条记录,导致性能下降。
在大数据量(例如超过 50 万行)或深度分页时,查询效率显著降低。
适用场景
数据量在 10 万级以内,或对性能不敏感的场景。
需要支持任意页码跳转的业务,如管理后台表格分页。
二、游标分页(基于ID或时间戳)
也称为“连续翻页”或“键集分页”,通过记录上一页的最后一条记录的ID(或时间戳)来实现下一页查询。
基本语法
SELECT * FROM table_name
WHERE id > 上次查询的最大id
ORDER BY id
LIMIT 20;
优点
避免了 OFFSET 的大量扫描,性能稳定。
适合顺序遍历大数据集,如数据同步、导出任务。
缺点
不支持随机跳转到指定页码。
必须有序且连续,如果ID跳跃大或存在空洞,仍可能扫描较多无效行。
在多条件筛选时,需确保排序字段能有效利用索引。
适用场景
数据导出、流式处理、消息队列消费等顺序读取场景。
不需要跳页,只需“上一页/下一页”的交互。
三、业务记录点位分页(联合索引分页)
可以看作是 LIMIT OFFSET 与 游标分页 的结合体,适用于多条件查询且数据量大的场景。
适用场景举例
假设要查询某一天范围内(如 2025-01-01 至 2025-02-01)的数据,数量在百万级别:
若使用 LIMIT OFFSET,深分页时会扫描大量无效行。
若仅使用 WHERE id > ?,由于数据可能非常老旧,ID 分布松散,仍会扫描很多行。
此时可建立联合索引 (update_time, id),分页查询时同时使用时间范围和ID游标:
基本语法
SELECT * FROM table_name
WHERE update_time >= '2025-01-01'
AND update_time < '2025-02-01'
AND id > 上一页最大id
ORDER BY update_time, id
LIMIT 20;
优点
结合条件过滤与游标,大幅减少扫描行数。
支持基于业务字段(如时间范围)的高效分页。
缺点
需要设计合适的联合索引。
业务逻辑稍复杂,需维护游标状态。
适用场景
按时间范围、状态等多条件分页查询。
大数据量报表生成、分批处理任务。
四、总结与建议
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| LIMIT OFFSET | 简单、支持跳页 | 深度分页性能差 | 小数据量或交互式分页 |
| 游标分页 | 性能好、适合顺序读取 | 不支持跳页、需有序连续 | 数据同步、顺序导出 |
| 业务记录点位分页 | 性能优、支持多条件 | 索引设计复杂、逻辑稍繁琐 | 大数据量多条件分页 |
建议
小数据量或后台管理系统可优先使用 LIMIT OFFSET,简单快捷。
数据导出或流式处理推荐使用游标分页,性能稳定。
大数据量且带条件查询应考虑业务记录点位分页,并合理设计联合索引。
在任何分页场景中,都应尽量避免 SELECT *,只查询需要的字段,并结合索引优化查询性能。