深分页与游标

1. 什么是深分页?

比如你有一个表,里面有 1000 万条数据,你想看第 10 万页,每页 10 条数据。

SQL 大概是这样:

sql 复制代码
SELECT * FROM user ORDER BY id LIMIT 1000000, 10;

这里的 1000000 就是偏移量(OFFSET),表示跳过前 100 万条,再取 10 条。

这种写法在数据量很大、页码很深的时候会特别慢,因为数据库必须先把前 100 万条数据找出来,再扔掉,最后只返回 10 条。即使有索引,它也要做很多无用功。


2. 怎么优化?

优化的核心思想就一句话:尽量让数据库不要跳过那么多数据

有两种最常用的基础方法:

方法一:记住上一页的最后一条数据,直接取下一页(游标)

假设你刚才看了第 1 页,最后一条记录的 id 是 100。那么看第 2 页的时候,不要用 OFFSET,而是直接取 id > 100 的 10 条:

sql 复制代码
SELECT * FROM user 
WHERE id > 100 
ORDER BY id 
LIMIT 10;

这里的100可以直接从前端传过来,这种方法叫游标

这样数据库直接从 id=101 开始往后取 10 条,不用跳,非常快。

缺点:这种写法只适合"上一页、下一页"这种翻页方式,不能直接跳到第 100 页。但大多数业务场景下,用户其实很少真的去点第 100 页,所以这种方法是性价比最高的。


方法二:先只查主键,再根据主键取完整数据

如果你必须支持跳页(比如用户可以直接输入页码),那就没办法避免偏移量。但我们可以让数据库在扫描的时候只扫描主键(或索引),少做点无用功。

原来的慢 SQL(先查所有字段,再跳 100 万行):

sql 复制代码
SELECT * FROM user ORDER BY id LIMIT 1000000, 10;

优化后的 SQL(分两步,但写成一个 SQL):

sql 复制代码
SELECT * FROM user 
WHERE id IN (
    SELECT id FROM user 
    ORDER BY id 
    LIMIT 1000000, 10
);

或者写成 JOIN 形式(更通用):

sql 复制代码
SELECT u.* 
FROM user u
JOIN (
    SELECT id FROM user 
    ORDER BY id 
    LIMIT 1000000, 10
) tmp ON u.id = tmp.id;

为什么这样快?

子查询 SELECT id FROM user ... 只查 id 列,如果 id 是主键,数据库可以只扫描索引,不用把整行数据都读出来,速度会快很多。然后外层再用这些 id 去取完整的行,因为 id 是主键,回表也是很快的。


3. 总结

  • 如果你能用"记住上一页的最后一条数据"的方法,就用它,这是最快最稳的。
  • 如果必须支持跳页,就用"先查主键,再回表"的方法。
  • 无论用哪种方法,都要保证 ORDER BY 的字段上有索引。

再举个例子:如果你的分页是按时间排序的,比如 ORDER BY create_time DESC,那么方法一就变成:

sql 复制代码
-- 上一页最后一条的时间是 '2024-01-01 10:00:00'
SELECT * FROM user 
WHERE create_time < '2024-01-01 10:00:00'
ORDER BY create_time DESC 
LIMIT 10;

方法二就改成:

sql 复制代码
SELECT u.* 
FROM user u
JOIN (
    SELECT id FROM user 
    ORDER BY create_time DESC 
    LIMIT 1000000, 10
) tmp ON u.id = tmp.id
ORDER BY u.create_time DESC;
相关推荐
瞬间&永恒~2 小时前
【MySQL】 InnoDB 锁等待排查与并发压测实验
运维·数据库·mysql
布鲁飞丝2 小时前
对 .NET线程 异常退出引发程序崩溃的反思
数据库·c#·.net
zcmodeltech2 小时前
智能矿井沙盘模型多系统协同控制系统设计:基于STM32与Modbus RTU的感知-传输-控制一体化方案
服务器·数据库·分布式·stm32·单片机·嵌入式硬件
江晓鱼未暖3 小时前
十七、Redis 核心原理与架构详解
大数据·数据库·数据仓库·redis·缓存·架构
copyer_xyf3 小时前
PostgreSQL 做向量检索:一条单库路线
数据库·agent·nestjs
Access开发易登软件3 小时前
Access 怎么做前后端分离?用 Web API 读写 SQL Server
前端·数据库·人工智能·microsoft·excel·access
Juicedata3 小时前
腾讯云 x JuiceFS:基于 FoundationDB 的企业级统一存储实践
数据库·人工智能·科技·云计算·腾讯云
小罗水4 小时前
第13章 Redis 缓存、幂等锁与任务状态
数据库·redis·缓存
SelectDB技术团队4 小时前
当 PostgreSQL 面临性能瓶颈:80TB 电商业务迁移至 Apache Doris 的实践思考
数据库·postgresql·apache
心念枕惊4 小时前
新写了个直播录制工具,可录制抖音快手斗鱼直播
运维·服务器·数据库