解决深分页问题

This commit is contained in:
2025-09-09 20:40:05 +08:00
parent e01e423714
commit 5b2dbfd379
6 changed files with 91 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -0,0 +1,81 @@
---
title: MySQL中如何解决深度分页问题
published: 2025-09-09
description: ''
image: ''
tags: ['MySQL', '分页','深度分页']
category: '中间件 > MySQL'
draft: false
lang: ''
---
# MySQL中如何解决深度分页问题
![](https://blog.meowrain.cn/api/i/2025/09/09/xp3bld-1.webp)
![](https://blog.meowrain.cn/api/i/2025/09/09/xh9385-1.webp)
## 问题描述
深度分页,指的是当数据量很大的时候,按照分页访问后面的数据,例如 `limit 9999990,10` 这会使得数据库要扫描前面的9999990条数据才能得到最终的10条数据大批量的扫描数据会增加数据库的负载影响性能。
## 三种优化方式
### 记录id
每次分页都返回当前的最大id然后下次查询的时候带上这个id就能利用id > maxid过滤了。
这种查询适合 连续查询的情况,如果跳页的话就不生效了。
普通分页的痛点:
```sql
SELECT * FROM article ORDER BY id LIMIT 10 OFFSET 100000;
```
MySQL 需要先扫描并跳过 前 100000 行,然后再返回后 10 行。(这里说下底层原因)
OFFSET 越大,性能越差。
我们每次查询时带上 上一次返回的最大 id下一页就只要取 id > last_id 的记录。
不依赖 OFFSET直接利用索引顺序扫描。
```sql
select * from products limit 0,10; -- 第一页
select * from products where id > 10 limit 10; -- 第二页
select * from products where id > 20 limit 10; -- 第三页
select * from products where id > 30 limit 10; -- 第四页
```
### 子查询
这里其实和记录id的优化方式是一样的只不过这里用的是子查询。理论上我们应该先去查询到上一页的最大id然后再查询下一页的数据。
```sql
SELECT * from products where id > (
SELECT id from products order by created_at desc limit 199999,1) order by created_at desc limit 10;
```
这里我们给表的created_at建索引可以利用created_at的二级索引进行扫描然后利用id > 上一次查询的最大id进行过滤最后再利用created_at的二级索引进行排序最后再利用limit进行分页。
![](https://blog.meowrain.cn/api/i/2025/09/09/xmphei-1.webp)
子查询只读索引列(最好覆盖:筛选列 + 排序列 + idIO 最小化。
外层 JOIN 回表范围仅为一页大小(如 20 条),成本可控。
相较于原来的
```sql
SELECT * FROM products order by created_at desc limit 200000,10;
```
这个虽然可以利用created_at的二级索引进行扫描但是它需要对每条记录进行一次回表操作还要丢弃掉前200000条记录性能较差。
![](https://blog.meowrain.cn/api/i/2025/09/09/xn0e7q-1.webp)
### join方法
```sql
SELECT p.* FROM products p INNER JOIN (
SELECT id FROM products ORDER BY created_at DESC limit 10 OFFSET 200000 ) AS page_results on p.id = page_results.id order by p.created_at desc;
```
这个和上面的子查询方式是一样的只不过这里用的是join。
### 使用es
直接上elasticsearch利用它本身分页的特性进行优化。

View File

@@ -0,0 +1,10 @@
---
title: MySQL事务的两阶段提交是什么
published: 2025-09-08
description: ''
image: ''
tags: []
category: ''
draft: false
lang: ''
---