文章目录
- [一、为什么 `LIMIT offset, size` 会慢到怀疑人生?](#一、为什么
LIMIT offset, size会慢到怀疑人生?) - 二、解决思路总览(先看地图)
- [三、王者方案:游标分页(Keyset Pagination)](#三、王者方案:游标分页(Keyset Pagination))
-
-
- [1️⃣ SQL 示例(最推荐)](#1️⃣ SQL 示例(最推荐))
- [2️⃣ 前端交互方式](#2️⃣ 前端交互方式)
- [3️⃣ 优点](#3️⃣ 优点)
- [4️⃣ 缺点](#4️⃣ 缺点)
- 四、必须支持"跳页"?那就用覆盖索引
-
- [1️⃣ 错误写法(慢)](#1️⃣ 错误写法(慢))
- [2️⃣ 正确写法(两步走)](#2️⃣ 正确写法(两步走))
- [3️⃣ 进一步优化(覆盖索引)](#3️⃣ 进一步优化(覆盖索引))
-
一、为什么 LIMIT offset, size 会慢到怀疑人生?
sql
SELECT * FROM orders
ORDER BY id
LIMIT 1000000, 20;
MySQL 的真实工作流程不是"直接跳到第 100 万条",而是:
-
从第一行开始扫描
-
丢掉前 1,000,000 行
-
再取 20 行
👉 offset 越大,丢的数据越多
👉 即使有索引,也要一路扫过去
所以:
超大分页 = 扫描 + 丢弃 + 心态爆炸
二、解决思路总览(先看地图)
| 方案 | 适合场景 | 性能 |
|---|---|---|
| 游标分页(Keyset) | 列表 / 无限滚动 | ⭐⭐⭐⭐⭐ |
| 子查询 + 覆盖索引 | 必须跳页 | ⭐⭐⭐⭐ |
| 记录最大页数 | 后台系统 | ⭐⭐⭐ |
| ES / Redis | 搜索 / 复杂排序 | ⭐⭐⭐⭐⭐ |
三、王者方案:游标分页(Keyset Pagination)
核心思想 :
👉 不要告诉数据库"我要第几页"
👉 告诉它"我要上一页最后一条之后的数据"
1️⃣ SQL 示例(最推荐)
sql
SELECT *
FROM orders
WHERE id > 1000000
ORDER BY id
LIMIT 20;
-
id必须是递增、有索引 -
前端传 lastId,而不是 page
2️⃣ 前端交互方式
-
第一次:不传 lastId
-
下一页:传上一次返回的
lastId
sql
{
"lastId": 1000000,
"pageSize": 20
}
3️⃣ 优点
-
🚀 性能稳定,和第几页无关
-
🚫 不扫描无用数据
-
✅ MySQL 最擅长这种查询
4️⃣ 缺点
-
❌ 不能随意跳到第 100 页
-
❌ 不适合"精确页码"的产品经理审美
📌 结论:
这是阿里、字节、美团后台列表的常规操作
四、必须支持"跳页"?那就用覆盖索引
如果产品经理坚持要"跳到第 500 页",那只能降低伤害。
1️⃣ 错误写法(慢)
sql
SELECT *
FROM orders
ORDER BY id
LIMIT 1000000, 20;
2️⃣ 正确写法(两步走)
sql
SELECT *
FROM orders
WHERE id >= (
SELECT id
FROM orders
ORDER BY id
LIMIT 1000000, 1
)
ORDER BY id
LIMIT 20;
3️⃣ 进一步优化(覆盖索引)
sql
SELECT o.*
FROM orders o
JOIN (
SELECT id
FROM orders
ORDER BY id
LIMIT 1000000, 20
) t ON o.id = t.id;
-
子查询只扫索引
-
回表次数极少
📌 注意 :
offset 再大也只是"相对能忍",不是本质解决