当前位置: 首页 > news >正文

记一次千万级订单表分页查询优化:

记一次千万级订单表分页查询优化:从 28 秒到 0.01 秒

背景

电商系统,delivery_orders表存放配送订单,累计数据量 1200 万行。运营后台有一个订单列表页,支持按商家 ID 筛选、按下单时间倒序翻页。

上线初期没什么问题,随着数据量增长,运营反馈"翻到后面几页就卡死了"。

问题复现

-- 运营翻到第 500 页时实际执行的 SQLSELECT*FROMdelivery_ordersWHEREseller_id=1001ORDERBYcreated_atDESCLIMIT10000,10;

执行时间:28.3 秒

而翻第 1 页只需要 0.03 秒,第 10 页 0.2 秒,越往后越慢,到第 1000 页直接超时。

排查过程

第一步:EXPLAIN 看执行计划

EXPLAINSELECT*FROMdelivery_ordersWHEREseller_id=1001ORDERBYcreated_atDESCLIMIT10000,10;

结果:

type: ref key: idx_seller_id rows: 487623 Extra: Using filesort

索引是走了,但rows = 487623,扫了将近 50 万行。更关键的是Using filesort,说明排序没走索引。

第二步:理解 LIMIT offset 的本质

很多人以为LIMIT 10000, 10是"跳过前 10000 条,取 10 条",实际上 MySQL 的执行过程是:

  1. 扫描满足WHERE seller_id = 1001的所有行
  2. created_at DESC排序
  3. 取出前 10010 行
  4. 丢掉前 10000 行,返回最后 10 行

也就是说,offset 越大,做的无用功越多。翻到第 1000 页就要取出 100010 行再丢掉 100000 行。

第三步:确认索引设计问题

SHOWINDEXFROMdelivery_orders;

现有索引:

  • idx_seller_id(seller_id)
  • idx_created_at(created_at)

两个单列索引,MySQL 只能用其中一个,排序和筛选没法同时走索引,所以出现了Using filesort

解决方案

方案一:建联合索引(治本)

ALTERTABLEdelivery_ordersADDINDEXidx_seller_created(seller_id,created_at);

联合索引让筛选和排序都走同一个索引,消除 filesort。

但 LIMIT offset 大的问题还没解决,继续优化。

方案二:子查询定位 ID,再回表取数据

-- 优化后的 SQLSELECT*FROMdelivery_ordersWHEREidIN(SELECTidFROMdelivery_ordersWHEREseller_id=1001ORDERBYcreated_atDESCLIMIT10000,10);

子查询只查id,走覆盖索引不需要回表,速度极快;外层再用id IN精确回表取完整数据,只回表 10 次。

执行时间:0.8 秒。有改善但还不够。

方案三:游标分页(彻底解决)

游标分页的思路是:不用 offset,而是记住上一页最后一条记录的游标值,下一页从游标位置开始取。

-- 第一页SELECTid,seller_id,created_at,statusFROMdelivery_ordersWHEREseller_id=1001ORDERBYcreated_atDESCLIMIT10;-- 假设第一页最后一条的 created_at = '2024-03-01 10:00:00',id = 98765-- 第二页:用游标替代 offsetSELECTid,seller_id,created_at,statusFROMdelivery_ordersWHEREseller_id=1001AND(created_at<'2024-03-01 10:00:00'OR(created_at='2024-03-01 10:00:00'ANDid<98765))ORDERBYcreated_atDESCLIMIT10;

这样每次查询都只扫描真正需要的行,不管翻到第几页,执行时间都是固定的。

配合idx_seller_created联合索引,执行时间:0.01 秒

最终方案落地

-- 1. 建联合索引ALTERTABLEdelivery_ordersADDINDEXidx_seller_created(seller_id,created_at);-- 2. 后端接口改为游标分页,接收参数:-- seller_id, last_created_at, last_id, page_size-- 3. 对外仍然支持"跳页"需求的场景,限制最大页数-- 超过 100 页引导用户缩小筛选条件

前端改动:将"页码"改为"加载更多"或"下一页"交互,产品上线后运营反馈正常,列表页无论翻多少页响应都在 50ms 以内。

总结

方案第500页耗时适用场景
原始 LIMIT offset28.3s不适用
子查询优化0.8s数据量不太大时可用
游标分页0.01s推荐,大数据量必选

分页慢的根本原因不是索引,是 LIMIT offset 的机制问题。大 offset 场景下,无论索引多完善都会慢,只有换掉分页方式才能真正解决。

http://www.cnnetsun.cn/news/1736614.html

相关文章:

  • win11 中文家庭版-无法下载更新-说是系统大补丁没有完全导致的-需要等到大补丁到了才可以下载更新
  • 从“僵尸节点”到优雅休眠:深入理解AUTOSAR NM中T_NM_Timeout的协同设计
  • Mac Mouse Fix:让10美元鼠标超越苹果触控板的终极解决方案
  • BUUCTF SQL注入通关秘籍:如何快速定位并利用fl4g表获取flag
  • 保姆级教程:在ZYNQ Ultrascale+ MPSOC上配置PS端DP显示(Vitis 2023.1实测)
  • 39_从工程角度分析:0_钢铁侠战甲的制造可行性
  • 树莓派/软路由必备:让frpc在OpenWrt或Debian系统开机自启的两种方法
  • 探索NomNom:定制《无人深空》游戏体验的全流程指南
  • MySQL实战:主键与外键的5个常见设计误区及优化方案
  • 如何用Listen1实现跨平台音乐播放?告别多平台切换的终极解决方案
  • 如何通过League Akari提升英雄联盟游戏体验:全方位效率工具指南
  • OpenClaw语音控制:Qwen3-4B-Thinking-2507-GPT-5-Codex-Distill-GGUF实现声控自动化
  • 渗透测试发现的Nacos漏洞怎么修?SpringBoot项目实战修复指南
  • 3步实现B站m4s格式转换:跨平台视频解决方案
  • 隐私·效率·低门槛:本地语音转文字工具TMSpeech的场景化指南
  • MiniCPM-V-2_6AR应用赋能:手机摄像头取景框实时图文叠加说明
  • 3分钟搞定抖音内容采集:开源工具如何颠覆传统下载方式?
  • 别再手动拖拽了!用Cursor+Claude 3.5生成Mermaid代码,5分钟搞定产品需求流程图
  • 终极B站视频下载指南:3步解锁4K大会员高清资源
  • 打造专属海拉鲁冒险:塞尔达传说旷野之息个性化存档编辑指南
  • 手把手教你用DeepSeek-OCR:图片文字提取保姆级教程
  • 别再死记命令了!深入理解RIP和OSPF协议差异,一次搞定动态路由配置
  • Node.js后端集成:快速配置环境并调用Qwen3.5-9B-AWQ-4bit模型API
  • 模型切换技巧:OpenClaw动态调用Qwen3-4B-Thinking不同量化版本
  • BOTW-Save-Editor-GUI:高效修改塞尔达传说旷野之息存档的实用工具
  • PyTorch 2.8镜像惊艳案例:建筑BIM模型→施工过程仿真视频自动生成
  • Pixel Aurora Engine 开发环境搭建:一站式配置 Anaconda 与项目依赖
  • OpenClaw+Qwen3.5-9B多模态实践:图文报告自动生成与归档
  • 利用快马AI快速原型:十分钟搭建你的简易版图拉丁工具箱
  • 利用快马ai一键生成burpsuite图文安装教程,快速搭建安全测试环境