MySQL数据库中如何高效处理千万级数据分页?

云墨同学_3180

云墨同学_3180

2026-07-27

820人浏览

原创

limit offset, size 在千万级表上卡死是因为mysql需扫描offset+size行并丢弃前offset行,导致大量无用io和cpu消耗。

mysql数据库中如何高效处理千万级数据分页?

为什么 LIMIT offset, size 在千万级表上会卡死?

因为 MySQL 不跳行,它老老实实从头开始扫描,数到 offset + size 行,再丢掉前 offset 行。比如 LIMIT 1000000, 10,就要读取并丢弃 100 万条记录——每条都可能触发一次回表(随机 IO),CPU、内存、磁盘全在干无用功。

常见错误现象:EXPLAIN 显示 type=range 或 type=index,但执行时间仍长达数秒甚至几十秒;SHOW PROFILE 显示大量 Handler_read_next 和 Handler_read_rnd_next;慢查询日志里反复出现同一类深分页语句。

  • 排序字段没索引 → 触发 filesort,千万级数据极易写磁盘临时文件
  • SELECT * + 非覆盖索引 → 每行都要回表,IO 成倍放大
  • 主键不连续或有删除 → id BETWEEN x AND y 这种“伪游标”会漏数据或重复

延迟关联(Deferred Join)怎么写才真正生效?

核心不是套个子查询,而是让子查询只走索引、不回表,并且外层 JOIN 只查 10 条(不是 100 万条)。

假设你查的是 t_order 表,按 create_time DESC 分页,且 status = 'PAID':

SELECT t1.* FROM t_order t1
INNER JOIN (
  SELECT id FROM t_order 
  WHERE status = 'PAID' 
  ORDER BY create_time DESC 
  LIMIT 1000000, 10
) t2 ON t1.id = t2.id;

关键前提必须满足:

  • 子查询里只查 id,不能带其他字段,否则无法走覆盖索引
  • 必须有联合索引,如 INDEX idx_status_ctime (status, create_time) —— 字段顺序不能错
  • 外层 t1.* 的字段数越多,网络和内存开销越大;能选具体字段就别用 *

如果没建对索引,这个写法照样慢。它只是把“扫 100 万行 + 回表”变成“扫 100 万行索引 + 10 次回表”,但扫索引本身仍有成本。

MySQL
MySQL

编写正确的MySQL查询,避免字符集、索引和锁方面的常见陷阱。

下载

游标分页(Seek Method)必须避开的三个坑

它快,是因为把 LIMIT offset 换成了 WHERE sort_col > last_value,MySQL 直接用索引定位起始点。但实际落地时容易栽在细节上。

  • 排序字段必须唯一:如果 create_time 有重复,WHERE create_time > '2023-01-01' 可能跳过同时间的多条记录,或重复返回 —— 必须补一个唯一字段,例如 ORDER BY create_time DESC, id DESC
  • 前端传参要校验:不能直接拼接用户传来的 last_id,得先查一遍确认该 ID 存在且属于当前查询条件范围内,否则可能越界或空结果
  • 不能用于跳页:比如用户点“第 500 页”,你没法用游标实现;这种场景只能退回到延迟关联,或加缓存预热

典型安全写法:

SELECT id, title, create_time FROM t_article 
WHERE status = 1 
  AND (create_time, id) > ('2023-10-01 12:00:00', 12345)
ORDER BY create_time DESC, id DESC 
LIMIT 20;

注意括号里的元组比较,比分开写 create_time >= ... AND id > ... 更严谨。

COUNT 总数要不要查?怎么查才不拖垮性能?

千万级表上,SELECT COUNT(*) FROM t WHERE ... 往往比查数据还慢,尤其带模糊匹配(LIKE '%xxx%')时,基本等于全表扫描。

  • 后台列表页如果非显示总数不可,优先用缓存:比如每小时异步跑一次 COUNT 写入 Redis,误差可接受
  • 用户搜索场景下,用 SQL_CALC_FOUND_ROWS 已被废弃,且在高并发下锁表风险高,别用
  • 全文检索能替代 LIKE:建 FULLTEXT 索引后,MATCH(...) AGAINST(...) 的 COUNT 效率提升百倍,但语义不同
  • 如果业务允许“估算”,可用 SHOW TABLE STATUS 的 rows 值(误差 ±10%),或采样估算(SELECT COUNT(*) * 10 FROM t TABLESAMPLE BERNOULLI (10))

最常被忽略的一点:前端分页控件显示“共 1234567 条”,对用户几乎无实际价值,反而逼你每次都要算总数。砍掉这个需求,性能立竿见影。

相关专题

更多
mysql修改数据表名
mysql修改数据表名

MySQL修改数据表:1、首先查看数据库中所有的表,代码为:‘SHOW TABLES;’;2、修改表名,代码为:‘ALTER TABLE 旧表名 RENAME [TO] 新表名;’。php中文网还提供MySQL的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

2253

6

MySQL创建存储过程
MySQL创建存储过程

存储程序可以分为存储过程和函数,MySQL中创建存储过程和函数使用的语句分别为CREATE PROCEDURE和CREATE FUNCTION。使用CALL语句调用存储过程智能用输出变量返回值。函数可以从语句外调用(通过引用函数名),也能返回标量值。存储过程也可以调用其他存储过程。php中文网还提供MySQL创建存储过程的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.21

1379

5

mongodb和mysql的区别
mongodb和mysql的区别

mongodb和mysql的区别:1、数据模型;2、查询语言;3、扩展性和性能;4、可靠性。本专题为大家提供mongodb和mysql的区别的相关的文章、下载、课程内容,供大家免费下载体验。

2023.07.18

815

5

mysql密码忘了怎么查看
mysql密码忘了怎么查看

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS 应用软件之一。那么mysql密码忘了怎么办呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.07.19

3132

5

mysql创建数据库
mysql创建数据库

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS 应用软件之一。那么mysql怎么创建数据库呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.07.25

5308

4

mysql默认事务隔离级别
mysql默认事务隔离级别

MySQL是一种广泛使用的关系型数据库管理系统,它支持事务处理。事务是一组数据库操作,它们作为一个逻辑单元被一起执行。为了保证事务的一致性和隔离性,MySQL提供了不同的事务隔离级别。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2023.08.08

1179

3

sqlserver和mysql区别
sqlserver和mysql区别

SQL Server和MySQL是两种广泛使用的关系型数据库管理系统。它们具有相似的功能和用途,但在某些方面存在一些显著的区别。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.11

5571

4

mysql忘记密码
mysql忘记密码

MySQL是一种关系型数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。那么忘记mysql密码我们该怎么解决呢?php中文网给大家带来了相关的教程以及其他关于mysql的文章,欢迎大家前来学习阅读。

2023.08.14

4882

7

mysql事务隔离级别
mysql事务隔离级别

mysql规范中定义了四种事务隔离级别,不同的隔离级别对事务的处理有所不同。本专题为大家提供mysql事务隔离级别相关的文章内容,大家可以免费体验。

2023.08.16

6474

11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL vs MySQL
PostgreSQL vs MySQL

共1课时 | 188人学习