0

0

MySQL怎样处理千万级数据分页 大表分页查询的优化方案

看不見的法師

看不見的法師

发布时间:2025-08-13 09:25:01

|

689人浏览过

|

来源于php中文网

原创

处理千万级数据分页的核心思路是避免使用limit offset,转而采用基于索引范围的游标分页。1. 使用自增主键或时间戳作为游标字段,首次查询通过order by和limit获取第一页数据,并记录最后一条记录的id;2. 查询下一页时,在where条件中加入游标字段大于上一页最后一个id的条件,结合order by和limit实现高效分页;3. 该方法利用索引快速定位起始位置,避免扫描和丢弃大量数据,显著提升性能;4. 局限在于仅支持“下一页”模式,无法直接跳转指定页码,适用于无限滚动等场景;5. 对于需跳页的后台系统,可采用延迟关联优化传统分页,或使用缓存、预计算总数等方式平衡性能与体验;6. 总数统计应避免实时select count(*),可通过缓存、估算或ui优化减少用户等待。此策略能有效解决大偏移量带来的性能瓶颈,是千万级数据分页的最优解。

MySQL怎样处理千万级数据分页 大表分页查询的优化方案

处理千万级数据分页,核心思路在于避免MySQL扫描并丢弃大量不必要的数据。简单粗暴的

LIMIT OFFSET
在数据量一大就彻底歇菜,因为它会从头开始数,数到你指定的偏移量,再取N条。真正的优化,通常是利用索引,将分页查询转化为基于范围的查询,比如使用上一页的最后一个ID作为下一页的起始点。

解决方案

解决千万级数据分页的根本之道,在于将传统的

LIMIT offset, limit
模式,转换为基于索引范围的查询。最常见且高效的策略是“游标分页”或“ID分页”。

具体来说,就是不使用

OFFSET
,而是利用表中的唯一递增字段(通常是主键ID,或者一个有序的时间戳字段)来定位下一页的起始位置。

首次查询:

SELECT id, column1, column2 FROM your_table
WHERE some_condition
ORDER BY id ASC
LIMIT 100;

这条查询会获取第一页的100条数据。你拿到这100条数据后,记录下其中最大的

id
值(比如是
max_id_on_current_page
)。

查询下一页:

SELECT id, column1, column2 FROM your_table
WHERE some_condition AND id > max_id_on_current_page
ORDER BY id ASC
LIMIT 100;

通过这种方式,MySQL可以直接利用

id
上的索引,快速定位到
max_id_on_current_page
之后的数据,避免了扫描前面大量的、你根本不需要的数据。如果需要倒序,逻辑类似,只是
ORDER BY id DESC
id < min_id_on_current_page

为什么简单的
LIMIT OFFSET
在千万级数据下会慢?

这问题,说实话,很多刚接触数据库优化的朋友都会踩坑。当你写下

SELECT * FROM large_table ORDER BY id LIMIT 1000000, 10;
这样的语句时,MySQL内部并不是“跳过”一百万条数据那么简单。它实际上是老老实实地从头开始,读取并处理了1000010条数据,然后才把前面的1000000条给扔掉,只留下最后10条给你。

这就像你让快递员从北京往上海送包裹,结果他得先把北京到南京的包裹都搬上车,再从车上扔下去,才开始送上海的。想象一下,如果偏移量(OFFSET)特别大,比如几百万、上千万,那这个“扔掉”的成本就变得极其高昂。即使你的

ORDER BY
字段有索引,MySQL也得遍历这个索引树,找到第1000001个位置,这本身就是个耗时的操作。尤其当
SELECT *
涉及大量列,或者数据行很宽时,每次读取并丢弃的IO和CPU开销都会成倍增加。这就是为什么简单分页在数据量大时,会让你感到绝望的原因。

基于游标(ID或时间戳)的分页优化实践

在我看来,基于游标的分页,是处理大表分页最优雅也最有效的方案之一。它本质上是把“跳过多少行”的逻辑,转换成了“从哪里开始取”的逻辑。

我们通常会利用表里一个连续递增且唯一的字段,比如自增主键ID,或者一个精确到毫秒的时间戳字段。

Peachly AI
Peachly AI

Peachly AI是一个一体化的AI广告解决方案,帮助企业创建、定位和优化他们的广告活动。

下载

具体做法:

  1. 初始查询: 第一次加载页面时,不带任何游标条件。

    -- 获取第一页数据,假设每页100条
    SELECT id, user_name, created_at FROM orders
    WHERE status = 'completed'
    ORDER BY id ASC
    LIMIT 100;

    从返回的结果中,取出最后一条记录的

    id
    值(例如是
    last_id_in_page
    )。

  2. 加载下一页: 将上一步获取到的

    last_id_in_page
    作为下一页的起始游标。

    -- 获取下一页数据
    SELECT id, user_name, created_at FROM orders
    WHERE status = 'completed' AND id > last_id_in_page
    ORDER BY id ASC
    LIMIT 100;

    MySQL会直接利用

    id
    上的索引,从
    last_id_in_page
    之后开始查找,效率极高。它只扫描需要返回的那些行,而不是从头开始扫描。

优点:

  • 性能卓越: 几乎只读取需要的数据,极大地减少了磁盘I/O和CPU消耗。
  • 稳定性高: 不受数据量增长的影响,性能曲线平稳。

局限性与考量:

  • 仅支持“下一页/上一页”模式: 这种方式无法直接跳到“第50页”,因为你不知道第49页的最后一个ID是什么。它更适合无限滚动、瀑布流或者简单的“下一页”导航。
  • 需要有序且唯一的游标字段: 如果你的表没有一个天然的、连续递增的唯一字段,可能需要额外引入一个,或者组合多个字段来模拟。
  • 排序问题: 如果你的排序字段不是游标字段,或者排序字段不唯一,可能会出现问题。例如,按
    created_at
    排序,如果同一秒内有大量记录,
    created_at > 'last_time'
    可能无法准确分页。这时,通常需要结合ID,形成复合条件:
    WHERE created_at >= 'last_time' AND id > 'last_id'
    ,或者
    WHERE (created_at = 'last_time' AND id > 'last_id') OR created_at > 'last_time'

结合业务场景,如何选择合适的分页策略?

选择分页策略,从来不是一个“一刀切”的问题,它得看你的业务场景到底需要什么。

场景一:瀑布流、无限滚动或简单“下一页”导航 这是最理想的情况,也是游标分页(ID或时间戳分页)大展身手的地方。用户通常只关心获取更多内容,而不是精确地跳到某一页。在这种情况下,ID分页的性能优势无可匹敌,因为它直接避免了大量无谓的数据扫描。

场景二:需要精确跳转到“第X页”的后台管理系统或报表 这场景就有点棘手了。用户可能需要输入页码直接跳转,或者看到总页数。

  • 方案A:小数据量或可接受的延迟 如果你的“千万级”只是偶尔出现,或者用户对等待时间有一定容忍度,那么传统的

    LIMIT OFFSET
    可能还能勉强用。但要做好心理准备,当偏移量变大时,查询会变得非常慢。

  • 方案B:延迟关联(Deferred Join / Delayed Join) 当无法使用ID分页,又必须使用

    OFFSET
    时,可以考虑这种优化。它的思路是:先在子查询中利用索引找到需要的那部分数据的
    id
    (或主键),然后用这些
    id
    再去关联主表,获取完整的行数据。

    SELECT t.*
    FROM your_table t
    INNER JOIN (
        SELECT id FROM your_table
        WHERE some_condition
        ORDER BY sort_column ASC
        LIMIT 1000000, 100
    ) AS subquery ON t.id = subquery.id;

    这样做的好处是,子查询

    SELECT id ...
    因为只取
    id
    ,通常会走覆盖索引(如果
    sort_column
    id
    都在一个索引里),或者至少避免了读取所有列的数据。主查询再根据少量
    id
    去精确查找,减少了全表扫描的开销。这比直接
    SELECT * LIMIT OFFSET
    要好,但本质上还是需要扫描
    offset + limit
    个索引项。

  • 方案C:缓存或预计算 对于那些不经常变动,但又需要频繁分页查询的报表数据,可以考虑将查询结果缓存到Redis、Memcached等内存数据库中,或者定期将查询结果预计算并存储到一个新的“快照表”中。这样,用户的查询直接命中缓存或快照表,速度飞快。但这种方案增加了系统的复杂性和数据一致性的挑战。

关于总页数或总记录数: 对于千万级数据,

SELECT COUNT(*)
本身就是个灾难。

  • 如果你不需要精确的总数,可以考虑近似值。例如,每隔一段时间更新一个缓存的总数,或者通过一些统计信息估算。
  • 如果必须精确,并且数据变化不大,可以*缓存`COUNT()`的结果,或者考虑异步计算**并在前端展示“正在加载总数”的状态。
  • 更激进一点,重新思考UI设计,是否真的需要总页数?很多产品,尤其是内容型产品,已经放弃了显示总页数,转而采用无限滚动。

没有银弹,每种方案都有其适用场景和局限。理解它们背后的原理,才能根据实际业务需求做出最合适的选择。

相关专题

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

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

653

2023.06.20

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

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

244

2023.06.21

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

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

280

2023.07.18

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

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

513

2023.07.19

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

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

250

2023.07.25

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

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

384

2023.08.08

sqlserver和mysql区别
sqlserver和mysql区别

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

523

2023.08.11

mysql忘记密码
mysql忘记密码

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

595

2023.08.14

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

74

2025.12.31

热门下载

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

精品课程

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

共48课时 | 1.6万人学习

MySQL 初学入门(mosh老师)
MySQL 初学入门(mosh老师)

共3课时 | 0.3万人学习

简单聊聊mysql8与网络通信
简单聊聊mysql8与网络通信

共1课时 | 779人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号