MySQL深分页进行性能优化的常见方法

2025-07-14 18:50

本文主要是介绍MySQL深分页进行性能优化的常见方法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《MySQL深分页进行性能优化的常见方法》在Web应用中,分页查询是数据库操作中的常见需求,然而,在面对大型数据集时,深分页(deeppagination)却成为了性能优化的一个挑战,在本文中,我们将...

引言:深分页,真的只是“翻页慢”那么简单吗?

在面试中,你是否遇到过这样的问题?

“你了解 mysql 深分页的性能问题吗?如何优化?”

又或者在真实项目中,当你翻到第 1000 页的数据时,接口突然变得异常缓慢,甚至超时崩溃。你打开慢 SQL 日志,看到那条熟悉的 LIMIT 100000, 20,心里默默叹了口气:“啊,又是深分页惹的祸。&rdChina编程quo;

作为一名有 8 年开发经验的 Java 工程师,我在多个后台系统和数据中心项目中都遇到过深分页带来的性能瓶颈。起初我们只是加索引、调 SQL,但最终编程发现:分页的本质,其实是数据访问策略的设计问题

这篇文章将带你深入理解:

  • 为什么深分页会拖垮数据库
  • 如何结合实际业务场景进行优化?
  • 有哪些可落地的代码实践?
  • 如何用一个高性能的游标分页方案替代传统分页?

一、背景介绍

在日常业务开发中,分页查询是非常常见的需求。例如在管理后台系统中,展示订单列表、用户列表、日志记录等都需要分页加载。

通常我们会使用类似下面的 SQL:

SELECT * FROM orders ORDER BY create_time DESC LIMIT 100000, 20;

上述查询语句的含义是:跳过 100000 条记录,取第 100001 到 100020 条数据。这种分页方式我们称为“深分页”

二、深分页的性能问题

MySQL 在执行 LIMIT offset, size 的时候,会先扫描 offset + size 条记录,然后抛弃前 offset 条,只返回 size 条。

也就是说,LIMIT 100000, 20 实际上扫描了 100020 行,仅返回 20 行。若表数据量和 offset 很大,性能将急剧下降,甚至拖垮数据库。

三、业务场景分析

假设你在做一个订单系统,运营人员需要查看历史订单数据,而这些订单数据量非常庞大(千万级),他们经常会翻到第 1000 页查看订单(每页 20 条)。

你发现系统在翻页到后面时接口响应非常慢,排查后定位到是 MySQL 查询耗时严重。

这时候我们就需要 优化深分页的查询方式

四、优化思路

方法一:使用覆盖索引

SELECT id FROM orders ORDER BY id LIMIT 100000, 20;
SELECT * FROM orders WHERE id IN (...);

通过先查主键,再回表查详细数据,减少回表成本。

方法二:记录上一次的游标(推荐)

使用**“基于游标的分页”**,也叫作“Keyset Pagination”或“Seek 方法”。

思路是:不要使用 LIMIT offset,而是通过上一次查询的最后一条数据的主键或排序字段作为游标,下次查询直接从该游标之后开始。

SELECT * FROM orders WHERE id > ? ORDER BY id ASC LIMIT 20;

优势:

  • 避免跳过大量数据,性能优越
  • 更适合实时数据流或按时间排序的场景

五、实战代码:Java 工具类实现游标分页

我们封装一个通用的分页工具类,支持游标分页,适用于 Spring + MyBatis/MyBatis-Plus 项目。

1. 分页请求参数类

public class CursorPageRequest {
    /**
     android* 游标字段,如 id、时间戳等
     */
    private Long cursor;

    /**
     * 每页数量
     */
    private int pageSize = 20;

    public CursorPageRequest() {}

    public CursorPageRequest(Long cursor, int pageSize) {
        this.cursor = cursor;
        this.pageSize = pageSize;
    }

    public Long getCursor() {
        return cursor;
    }

    public void setCursor(Long cursor) {
        this.cursor = cursor;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }
}

2. 通用分页返回类

import jChina编程ava.util.List;

public class CursorPageResponse<T> {
    private List<T> data;
    private Long nextCursor;
    private boolean hasMore;

    public CursorPageResponse(List<T> data, Long nextCursor, boolean hasMore) {
        this.data = data;
        this.nextCursor = nextCursor;
        this.hasMore = hasMore;
    }

    public List<T> getData() {
        return data;
    }

    public Long getNextCursor() {
        return nextCursor;
    }

    public boolean isHasMore() {
        return hasMore;
    }
}

3. MyBatis 示例 Mapper 接口(以订单为例)

@Mapper
public interface OrderMapper {

    /**
     * 查询游标分页数据
     * @param cursor 上次最后一条记录的 ID
     * @param pageSize 每页数量
     */
    @Select("SELECT * FROM orders " +
            "WHERE (:cursor IS NULL OR id > #{cursor}) " +
            "ORDER BY id ASC LIMIT #{pageSize}")
    List<OrderDO> listByCursor(@Param("cursor") Long cursor, @Param("pageSize") Integer pageSize);
}

4. Service 层封装分页逻辑

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    public CursorPageResponse<OrderDO> listOrdersByCursor(CursorPageRequest request) {
        List<OrderDO> list = orderMapper.listByCursor(request.getCursor(), request.getPageSize());

        Long nextCursor = null;
        boolean hasMore = false;

        if (!list.isEmpty()) {
            // 获取最后一条记录的 ID 作为下次游标
            nextCursor = list.get(list.size() - 1).getId();
            hasMore = list.size() == request.getPageSize();
        }

        return new CursorPageResponse<>(list, nextCursor, hasMore);
    }
}

六、接口调用示例

前端每次请求:

GET /orders?cursor=10021&pageSize=20

返回:

{
  "data": [...],
  "nextCursor": 10041,
  "hasMore": true
pEDfaVpBoM}

前端将 nextCursor 作为下次请求的 cursor 参数实现“加载更多”效果。

七、总结

分页方式优点缺点
LIMIT offset, size简单通用深分页性能差
游标分页(Keyset)性能优秀,避免跳过大量数据不支持跳页,只支持顺序加载

建议:在数据量大、分页页码深的业务场景中,优先使用 游标分页,尤其是管理后台、数据分析系统、接口服务等。

八、建议与扩展

  • 游标字段最好是唯一递增的,比如主键 ID、时间戳等
  • 多字段组合游标可以支持更复杂的排序场景
  • 可以将分页封装为 AOP 或 BaseService 通用组件

以上就是MySQL深分页进行性能优化的常见方法的详细内容,更多关于MySQL深分页性能优化的资料请关注编程China编程(www.chinasem.cn)其它相关文章!

这篇关于MySQL深分页进行性能优化的常见方法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1155379

相关文章

深度解析Java @Serial 注解及常见错误案例

《深度解析Java@Serial注解及常见错误案例》Java14引入@Serial注解,用于编译时校验序列化成员,替代传统方式解决运行时错误,适用于Serializable类的方法/字段,需注意签... 目录Java @Serial 注解深度解析1. 注解本质2. 核心作用(1) 主要用途(2) 适用位置3

MySQL中On duplicate key update的实现示例

《MySQL中Onduplicatekeyupdate的实现示例》ONDUPLICATEKEYUPDATE是一种MySQL的语法,它在插入新数据时,如果遇到唯一键冲突,则会执行更新操作,而不是抛... 目录1/ ON DUPLICATE KEY UPDATE的简介2/ ON DUPLICATE KEY UP

MySQL分库分表的实践示例

《MySQL分库分表的实践示例》MySQL分库分表适用于数据量大或并发压力高的场景,核心技术包括水平/垂直分片和分库,需应对分布式事务、跨库查询等挑战,通过中间件和解决方案实现,最佳实践为合理策略、备... 目录一、分库分表的触发条件1.1 数据量阈值1.2 并发压力二、分库分表的核心技术模块2.1 水平分

Python与MySQL实现数据库实时同步的详细步骤

《Python与MySQL实现数据库实时同步的详细步骤》在日常开发中,数据同步是一项常见的需求,本篇文章将使用Python和MySQL来实现数据库实时同步,我们将围绕数据变更捕获、数据处理和数据写入这... 目录前言摘要概述:数据同步方案1. 基本思路2. mysql Binlog 简介实现步骤与代码示例1

504 Gateway Timeout网关超时的根源及完美解决方法

《504GatewayTimeout网关超时的根源及完美解决方法》在日常开发和运维过程中,504GatewayTimeout错误是常见的网络问题之一,尤其是在使用反向代理(如Nginx)或... 目录引言为什么会出现 504 错误?1. 探索 504 Gateway Timeout 错误的根源 1.1 后端

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

Python实战之SEO优化自动化工具开发指南

《Python实战之SEO优化自动化工具开发指南》在数字化营销时代,搜索引擎优化(SEO)已成为网站获取流量的重要手段,本文将带您使用Python开发一套完整的SEO自动化工具,需要的可以了解下... 目录前言项目概述技术栈选择核心模块实现1. 关键词研究模块2. 网站技术seo检测模块3. 内容优化分析模

使用shardingsphere实现mysql数据库分片方式

《使用shardingsphere实现mysql数据库分片方式》本文介绍如何使用ShardingSphere-JDBC在SpringBoot中实现MySQL水平分库,涵盖分片策略、路由算法及零侵入配置... 目录一、ShardingSphere 简介1.1 对比1.2 核心概念1.3 Sharding-Sp

Java实现复杂查询优化的7个技巧小结

《Java实现复杂查询优化的7个技巧小结》在Java项目中,复杂查询是开发者面临的“硬骨头”,本文将通过7个实战技巧,结合代码示例和性能对比,手把手教你如何让复杂查询变得优雅,大家可以根据需求进行选择... 目录一、复杂查询的痛点:为何你的代码“又臭又长”1.1冗余变量与中间状态1.2重复查询与性能陷阱1.

Python内存优化的实战技巧分享

《Python内存优化的实战技巧分享》Python作为一门解释型语言,虽然在开发效率上有着显著优势,但在执行效率方面往往被诟病,然而,通过合理的内存优化策略,我们可以让Python程序的运行速度提升3... 目录前言python内存管理机制引用计数机制垃圾回收机制内存泄漏的常见原因1. 循环引用2. 全局变