深分页性能问题分析与优化实践

在日常测试工作中,我们经常会遇到分页查询接口,例如:

GET /product/search?keyword=&pageNum=1&pageSize=10

乍看之下,这样的分页接口似乎并无性能问题,响应时间也很快。但在一次性能压测中,我们复现了一个典型的深分页性能瓶颈,并深入分析了其成因与优化思路,本文记录该过程与结论。

📌 压测背景

接口路径:/product/search

功能描述:根据关键字模糊查询商品列表,支持分页(pageNum, pageSize)。

✅ 数据规模
为了模拟真实生产场景,我们使用以下 SQL 批量造数,构造了 100万+商品数据(pms_product 表):


SET @max_id := (SELECT IFNULL(MAX(id), 0) FROM pms_product);
SET @row := 0;INSERT INTO pms_product (id, brand_id, product_category_id, name, sub_title, price,publish_status, verify_status, sort, description, delete_status,new_status, recommand_status, sale, stock, low_stock, unit, weight,preview_status, service_ids, keywords, note, product_sn
)
SELECT @max_id + seq AS id,FLOOR(1 + RAND() * 10),FLOOR(1 + RAND() * 10),CONCAT('商品-', @max_id + seq),'',ROUND(RAND() * 1000, 2),1, 1, @max_id + seq, '', 0, 1, 1, 100, 100, 10, '', 1.5, 1,'1,2,3', '', '', CONCAT('SN', LPAD(@max_id + seq, 6, '0'))
FROM (SELECT @row := @row + 1 AS seqFROM (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t1,(SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t2,(SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t3,(SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t4,(SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t5,(SELECT @row := 0) rLIMIT 100000
) temp;

数据字段包含多个维度如:商品分类、品牌、价格、上下架状态、是否删除等。

🔍 性能压测结果对比

我们使用 JMeter 对分页接口进行了压测,以下是对比结果:
正常分页压测结果:
在这里插入图片描述
深分页压测结果:
在这里插入图片描述

🚩 分页页码:pageNum=1(正常分页)

样本数平均响应时间最大响应时间吞吐量 (TPS)平均返回字节数
102263ms2419ms3.2/sec9317 字节

🚩 分页页码:pageNum=100000(深分页)

样本数平均响应时间最大响应时间吞吐量 (TPS)平均返回字节数
103245ms3653ms2.4/sec4224 字节

🧠 为什么深分页会变慢?
❗ 1. Offset 越大,代价越高
分页底层使用 LIMIT offset, size,如:
1)深分页执行的sql:
在这里插入图片描述


SELECT * FROM pms_product 
WHERE delete_status = 0 AND publish_status = 1 
ORDER BY id 
LIMIT 99990, 10;

该查询需要:

  • 遍历前 100000 行(offset),丢弃
  • 返回最后 10 行

即使加了索引,MySQL 也必须扫描 offset + limit 条数据后再丢弃前面。
2)正常分页执行的sql:
在这里插入图片描述

SELECT count(0) FROM pms_product WHERE delete_status = 0 AND publish_status = 1;
SELECT ... FROM pms_product WHERE delete_status = 0 AND publish_status = 1 LIMIT 10;

✅ 结论:

  • LIMIT 10 是在数据前面截取的,性能还行,Rows_examined扫描了28行;
  • count(0) 已扫描 100w 行(较慢);

❗ 2. explain 显示没有使用覆盖索引
我们对深分页 SQL 执行了 EXPLAIN 分析:


EXPLAIN SELECT * FROM pms_product 
WHERE delete_status = 0 AND publish_status = 1 
ORDER BY id LIMIT 99990, 10;

在这里插入图片描述

❗ 问题分析:

字段说明
type=index说明是走了索引,但是全索引扫描(index scan),相当于扫描整张表的索引部分。
key=PRIMARY表示使用的是主键索引(id)。
rows=100010MySQL 预估会扫描大约 10 万行来定位 LIMIT 起始位置。
Extra=Using where表示 WHERE 条件在过滤过程中才判断,并没有用到复合索引来提前过滤。

⚠️ 这意味着:

  • LIMIT 100000, 10 会导致 MySQL 扫描超过 10 万条记录,性能非常差。
  • WHERE 条件没有使用到合适的索引(possible_keys 为 NULL)。

✅ 性能优化建议
1. 避免深分页 —— 改用“基于游标”方式
例如前端传入上一次返回结果的 last_id,实现类似“加载更多”:


SELECT * FROM pms_product 
WHERE delete_status = 0 AND publish_status = 1 AND id > 上一次最大 id
ORDER BY id
LIMIT 10;

优点:

  • 避免 offset,性能线性增长
  • 可以用覆盖索引,避免回表
  • 建立合理的联合索引
    如分页条件为:

WHERE delete_status = 0 AND publish_status = 1 ORDER BY id

建议加:


CREATE INDEX idx_status_id 
ON pms_product(delete_status, publish_status, id);

这样可以走索引,减少扫描行数。

3. 考虑分页缓存
如果某些页经常访问,可以考虑将分页结果缓存到 Redis,提升响应速度。

📝 总结

深分页是一种常见但代价昂贵的分页方式,特别在数据量大、页码大的时候:

  • offset 会严重拖慢查询
  • 即使不使用 count(),深分页依然很慢
  • 优化建议包括:改游标分页、加索引、用缓存等

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/90707.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/90707.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode——1957. 删除字符使字符串变好

通过万岁!!! 题目:给你一个字符串,然后让你删除几个字符串,让他变成好串,好串的定义就是不要出现连续的3个一样的字符。思路:首先就是要遍历字符串。我们将要返回的字符串定义为ret&…

Aerospike与Redis深度对比:从架构到性能的全方位解析

在高性能键值存储领域,Aerospike与Redis是两款备受关注的产品。Redis以其极致的单机性能和丰富的数据结构成为主流选择,而Aerospike则凭借分布式原生设计和混合存储架构在大规模场景中崭露头角。本文将从架构设计、数据模型、性能表现、扩展性等核心维度…

Linux命令速查手册

一、命令格式与辅助工具类别符号/命令示例说明基本格式commandls -a /home命令 选项 参数管道符ls -lless重定向>df -h > disk_usage.txt覆盖写入文件>>echo "New" >> notes.txt追加写入文件2>ls non_exist 2> error.txt错误输出重定向快捷…

net-snmp添加自定义mib树

首先我们把前面mib2c生成的文件修改 下面重新做了个简单点的MIB树 -- -- -- MIB generated by MG-SOFT Visual MIB Builder Version 6.0 Build 88 -- Saturday, July 26, 2025 at 09:24:54 --ARHANGELSK-GLOBAL-REG DEFINITIONS :: BEGINIMPORTSenterprises, OBJECT-TYPE, M…

【动态规划-斐波那契数列模型】理解动态规划:斐波那契数列的递推模型

算法相关知识点可以通过点击以下链接进行学习一起加油!动态规划是一种解决最优化问题的强大技术,通过将问题分解为子问题并逐步求解来实现高效计算。斐波那契数列是动态规划中经典的应用之一,其递推关系非常适合用动态规划进行优化。通过动态…

微信小程序 自定义带图片弹窗

1. 微信小程序 自定义带图片弹窗1.1. 实现思路使用官方组件实现图片模态弹窗。首先找到官方文档:​显示模态弹窗的API wx.showModal(OBJECT)wx.showModal参数介绍发现并没有设置图片的参数,但是这是一个API,但是组件呢?我并没有在…

私有化大模型架构解决方案构建指南

内容概要本指南旨在为企业提供私有化大模型架构解决方案的全面构建路径,帮助其在保障数据隐私的同时提升业务效率。我们将系统解析关键环节,包括安全部署策略设计、模型训练核心技术、持续优化机制构建以及知识管理实践路径。此外,指南还涵盖…

面试150 查找和最小的K对数字

思路1 超时法:通过两个循环记录三元组[num1,num2,num1num2]然后通过num1num2从小到大进行排序,然后返回前K个对数中的前两个数即可。 class Solution:def kSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:if n…

vscode目录,右键菜单加入用VSCode打开文件和文件夹(快速解决)(含删除)(脚本)

1.创建文本文件 在桌面右键单击,选择“新建” > “文本文档”,将其命名为“vscode.txt”2.复制代码内容3.修改文件扩展名 右键单击“vscode.txt”文件,选择“重命名”,将文件扩展名从.txt改为.reg,使其成为“vscode…

Chart.js 柱形图详解

Chart.js 柱形图详解 引言 在数据可视化领域,柱形图是一种非常常见的图表类型,它能够直观地展示不同类别或组的数据之间的比较。Chart.js 是一个基于 HTML5 Canvas 的开源库,它提供了一系列的图表绘制功能,其中包括柱形图。本文将…

沉浸式文旅新玩法-基于4D GS技术的真人数字人赋能VR体验升级

线下沉浸式剧场与 LBE VR 相结合,会碰撞出什么样的火花?本次 PICO 视频、东方演艺集团与火山引擎一起,将沉浸式演出《只此周庄》的部分场景复刻到了 VR 世界,让用户在虚拟的古代周庄夜市里,体验了古老的故事以及精彩纷…

C程序内存布局详解

C程序内存布局详解 1. 内存布局概述 C程序在内存中分为以下几个主要区域(从低地址到高地址): 代码段(.text)只读数据段(.rodata)初始化数据段(.data)未初始化数据段&…

新手向:Git下载全攻略

Git 的安装与重要性在现代软件开发中,版本控制是必不可少的工具,而 Git 是目前最流行的分布式版本控制系统。无论是个人开发者还是大型团队,Git 都能高效管理代码变更,确保项目历史清晰可追溯。安装 Git 是开发者入门的第一步&…

linux中如何清除history命令

写在前面 使用ssh远程连接客户端连接上linux后操作的命令多了,有时候需要清除对应的历史命令记录,可以通过下面几种方式实现。第一种方法 通过修改.bash_history文件 这是最简单直接的方法,但是只会影响当前用户的历史记录。执行以下命令即可…

PHP插件开发中的一个错误:JSON直接输出导致网站首页异常

问题描述 最近在使用步数统计插件&#xff08;WeFootStep&#xff09;时&#xff0c;发现网站首页完全变成了一段JSON数据&#xff0c;而不是正常的HTML页面。具体表现为首页显示如下内容&#xff1a; {"results":"<li><a href\"https:\/\/blog…

落霞归雁的思维框架:十大经典思维工具的源头活水

在当今复杂多变的世界中&#xff0c;思维框架成为了解决问题、优化决策和提升效率的重要工具。提到思维框架&#xff0c;人们往往会想到那些被广泛认可和应用的十大经典思维工具&#xff1a;金字塔原理、黄金圈法则、5W1H分析法、SWOT分析、SCQA模型、STAR法则、PDCA循环、六顶…

spring Could 高频面试题

一、基础概念Spring Cloud 的核心组件有哪些&#xff1f; 答案&#xff1a;Eureka/Nacos&#xff08;服务注册发现&#xff09;、Ribbon/LoadBalancer&#xff08;负载均衡&#xff09;、Feign/OpenFeign&#xff08;声明式HTTP客户端&#xff09;、Hystrix/Sentinel&#xff0…

从零开始的云计算生活——番外6,使用zabbix对中间件监控

目录 一.网络设备监控 1、GNS模拟器的使用 创建路由 创建交换机 2.构建网络 3.添加Cisco路由器的监控 二.中间件监控 1、MySQL数据库监控 1.1、拷贝自定义的监控脚本到指定目录 1.2、添加监控用户 1.3、重启zabbix-agent服务 1.4、在zabbix-server服务端测试数据 1…

haproxy七层均衡

一.haproxy的安装和服务信息1.1实验环境ip实验设备172.25.254.100haproxy172.25.254.10RS1172.25.254.20RS2172.25.254.111client1.2软件安装及配置haproxy主机上配置#下载#进入此文件进行编辑#关闭防火墙RS1主机上配置#下载#生成默认文件#重启#关闭防火墙RS2主机上配置#下载#生…

分类预测 | MATLAB实现CPO-SVM冠豪猪算法优化支持向量机分类预测

分类预测 | MATLAB实现CPO-SVM冠豪猪算法优化支持向量机分类预测 目录 分类预测 | MATLAB实现CPO-SVM冠豪猪算法优化支持向量机分类预测 分类效果 基本介绍 算法步骤 参数设定 运行环境 应用场景 程序设计 参考资料 分类效果 基本介绍 该MATLAB代码实现了基于冠豪猪优化算法(…