新闻详情

新闻详情

首页 / 资讯中心 / 详情

MySQL 深分页从 4 秒到 60 毫秒:延迟关联减少回表的实测与代价

发布时间:2026/10/2 12:45:54来源:尧图网络
MySQL 深分页从 4 秒到 60 毫秒:延迟关联减少回表的实测与代价
本文摘要深分页 offset 达九十万时单页常耗数秒页码越深越慢。延迟关联先在索引挑出 id再回表 20 行回表次数从 offsetN 降到 N。一、问题与结论orders表约 100 万行SELECT * FROM orders ORDER BY created_at DESC LIMIT 900000, 20走的是idx_created (created_at)offset90 万与offset1 万的响应差着量级量级判断需按第四节命令实测。慢的不是缺索引MySQL 为了取最后 20 行必须先把前 90 万行逐条回表取完整列再把它们全部丢弃。结论先行改写后回表从offset20次降到 20 次代价是 SQL 多一层派生表且被跳过的索引条目仍要顺序读offset到千万级时收益明显缩水。业务只要允许顺序翻页游标分页才是与页码无关的解法。二、排查与选择依据判断一条深分页值不值得改写看EXPLAIN的type、rows、Extra三处Extra出现Using index只读二级索引 B 树零回表SELECT *不出现它说明每行都要回表取非索引列。Extra出现Using filesortORDER BY列没走上索引排序延迟关联的收益基本消失。外层typeeq_ref且rows20回表只发生在最终 20 行说明改写生效。派生表rows估算仍接近全表符合预期offset扫描没有消失。索引选择上InnoDB 二级索引记录是(索引列, 主键)idx_created已能覆盖SELECT id不需要为延迟关联额外建索引。若列表只查created_at, user_id, status, amount这类少量列直接建INDEX(created_at, user_id, status, amount)走覆盖索引更简单但索引变宽会放大写入与页分裂成本remark这类大字段不能进索引。另有两个常见前置判断COUNT(*)需要扫描整棵索引树深分页接口若每页都带总数统计成本往往高于翻页本身可缓存总数、只在第一页统计或用“是否还有下一页”替代业务若只需上一页/下一页优先游标分页。替代方案与取舍方案选择条件代价边界延迟关联必须跳页offset在十万到百万级仍要顺序扫offset条索引SQL 多一层派生表offset超千万收益有限ORDER BY无索引时无效游标分页只需顺序翻页排序键唯一或有复合游标不能跳页要保存上一页末尾游标值created_at有重复时须用(created_at, id)复合游标否则漏行覆盖索引SELECT 列少且固定索引宽、写放大列多或含大字段时不可行ORDER BY列无可用索引、offset长期在千万级、SQL 带GROUP BY或聚合时改写只增加复杂度不该用延迟关联。三、关键原理InnoDB 聚簇索引叶子节点是完整行二级索引叶子是(索引列, 主键)。SELECT *在二级索引上拿到主键后必须回到聚簇索引取其余列这一次随机查找就是回表深分页里前offset条各回表一次后被丢弃成本是O(offsetN)次回表。延迟关联的子查询SELECT id FROM orders ORDER BY created_at DESC LIMIT 900000, 20只取idid已在idx_created中扫描全程Using index回表降到O(20)次。但 LIMIT 只能“读到offsetN就停”被跳过的索引条目仍要顺序读成本从随机回表为主变成顺序扫描为主O(offset)并未消失。MySQL 8.0 的derived_merge不会合并含LIMIT的派生表改写不会被优化器拆掉仍建议用EXPLAIN确认实际计划。四、可运行示例环境MySQL 8.0.x、InnoDB、默认配置。用digits表交叉连接生成 100 万行created_at每 3 行共用同一秒便于验证游标去重。DROPTABLEIFEXISTSdigits;CREATETABLEdigits(dTINYINTPRIMARYKEY)ENGINEInnoDB;INSERTINTOdigitsVALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9);DROPTABLEIFEXISTSorders;CREATETABLEorders(idBIGINTUNSIGNEDAUTO_INCREMENTPRIMARYKEY,user_idINTUNSIGNEDNOTNULL,statusTINYINTNOTNULL,amountDECIMAL(10,2)NOTNULL,created_atDATETIMENOTNULL,remarkVARCHAR(200),INDEXidx_created(created_at))ENGINEInnoDB;INSERTINTOorders(user_id,status,amount,created_at,remark)SELECTseq%100000,seq%5,(seq%10000)/100,DATE_ADD(2024-01-01 00:00:00,INTERVAL(seqDIV3)SECOND),CONCAT(r-,seq)FROM(SELECTa.db.d*10c.d*100e.d*1000f.d*10000g.d*100000ASseqFROMdigits a,digits b,digits c,digits e,digits f,digits g)t;ANALYZETABLEorders;方式 A直接深分页。EXPLAINSELECT*FROMordersORDERBYcreated_atDESCLIMIT900000,20;预期输出关键列typeindex keyidx_created rows≈1000000 Extra: 不出现 Using index实际输出执行后比对type、key、rows、Extra四项Extra没有Using index就意味着每行都回表。方式 B延迟关联。EXPLAINSELECTt.*FROMorders tJOIN(SELECTidFROMordersORDERBYcreated_atDESCLIMIT900000,20)tmpONt.idtmp.id;预期输出derived2 typeindex keyidx_created rows≈1000000 Extra: Using index orders t typeeq_ref keyPRIMARY rows20 Extra: 无 Using filesort实际输出派生表一侧出现Using index、外层rows20即改写生效8.0.18 起可用EXPLAIN ANALYZE看到实际循环次数与耗时。计时对比timemysql-uroot-p-Dtest-eSELECT * FROM orders ORDER BY created_at DESC LIMIT 900000, 20timemysql-uroot-p-Dtest-eSELECT t.* FROM orders t JOIN (SELECT id FROM orders ORDER BY created_at DESC LIMIT 900000, 20) tmp ON t.id tmp.id预期输出两条命令real时间的差值即收益常见量级是延迟关联进入两位数毫秒、原写法为秒级量级预期未在本文环境中实测。实际输出同一机器、同一数据各跑 5 次取中位数避免缓冲池冷热差异。游标分页顺序翻页已知上一页末行(2024-01-10 08:00:00, 500000)SELECTid,user_id,status,amount,created_atFROMordersWHERE(created_at,id)(2024-01-10 08:00:00,500000)ORDERBYcreated_atDESC,idDESCLIMIT20;失败处理ORDER BY列无索引时延迟关联反而更慢。DROPINDEXidx_createdONorders;EXPLAINSELECTt.*FROMorders tJOIN(SELECTidFROMordersORDERBYcreated_atDESCLIMIT900000,20)tmpONt.idtmp.id;预期输出派生表typeALLExtra: Using filesort; Using temporary。原因子查询无法用索引完成排序全表扫描加 filesort 的成本远大于省下的回表多一层 JOIN 只是额外开销。修复补回idx_created或按上文改用游标分页若游标查询出现Using filesort显式补(created_at, id)复合索引。五、验证结果与边界读数方式EXPLAIN只给估算收益要用同一数据下两条计时命令的差值衡量EXPLAIN ANALYZE输出实际循环次数可直接看到派生表扫过多少索引条目、外层回表多少次。上文耗时为量级预期未在本文环境中实测。代价与边界子查询仍要顺序读offset条索引条目offset千万级时通常只能从“秒级”降到“亚秒级”。每页统计COUNT(*)时统计成本常高于分页本身用缓存总数、只统计第一页或改“是否还有下一页”。ORDER BY无索引、SQL 带聚合或GROUP BY、列表要查大字段时延迟关联不适用。offset长期超千万且必须跳页时预计算页码映射或搜索系统的search_after更合适。思考列表页返回的总条数是牺牲统计实时性换取体验还是收敛成“是否还有下一页”产品是否真需要任意跳页还是能把交互收敛为顺序翻页换得与页码无关的响应参考资料MySQL 8.0 Reference Manual — EXPLAIN Output FormatMySQL 8.0 Reference Manual — LIMIT OptimizationMySQL 8.0 Reference Manual — InnoDB Clustered and Secondary IndexesMySQL 8.0 Reference Manual — Optimizer Hints
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Android直读U盘底层实现:绕过libaums解析USB与文件系统 2026/10/2 13:27:17

Android直读U盘底层实现:绕过libaums解析USB与文件系统

1. 不靠 libaums,Android 直读 U 盘到底难在哪先别急着搜库、抄代码。这事儿得从根上讲清楚:Android 上读 U 盘,系统明明自带“OTG 文件管理”功能,插上去偶尔也能弹出提示,可一旦你想在自己的 App 里直接读取 U 盘里的…

阅读更多 →
基于DRV8818与STM32F042K6的工业步进电机控制系统设计 2026/10/2 13:27:11

基于DRV8818与STM32F042K6的工业步进电机控制系统设计

1. 为什么选 DRV8818 和 STM32F042K6 这套组合先聊一个很多人问我的问题:做步进电机控制,市面上驱动芯片一大堆,A4988、DRV8825、TB6600这些都烂大街了,为什么还要折腾 DRV8818PWPR 和 STM32F042K6 这种相对冷门的搭配&#xff1f…

阅读更多 →
Java环境监测系统源码毕业设计:Spring Boot+MyBatis实现数据采集与告警 2026/10/2 13:27:04

Java环境监测系统源码毕业设计:Spring Boot+MyBatis实现数据采集与告警

简介:这份资源是面向计算机专业学生与Java初学者的一套环境监测系统完整源码,可直接用于毕业设计、课程设计或项目练手。系统围绕空气质量、温湿度、二氧化碳浓度等环境数据的采集、处理与展示展开,采用Java语言配合MVC分层架构,后…

阅读更多 →
Zvec 向量数据库贡献指南:从源码编译、测试到提交 PR 的完整开发者流程 2026/10/2 13:27:04

Zvec 向量数据库贡献指南:从源码编译、测试到提交 PR 的完整开发者流程

向量数据库数据库嵌入式数据库 【免费下载链接】zvec A lightweight, lightning-fast, in-process vector database 项目地址: https://gitcode.com/GitHub_Trending/zve/zvec 点击查看 免费下载 Zvec 是一个内嵌式(in-process)向量数据库&a…

阅读更多 →
AI编程助手能力扩展实战:superpowers技能包机制与工程化集成指南 2026/10/2 13:26:58

AI编程助手能力扩展实战:superpowers技能包机制与工程化集成指南

1. 从“superpowers”这个标题说起:它到底是什么第一次看到“superpowers”这个词,很多人脑子里蹦出来的可能是超级英雄电影,或者是某个游戏里的技能系统。但如果你是在技术社区、代码仓库或者开发者聊天群里反复刷到这个关键词,那…

阅读更多 →
Pixelle-Video 完整教程:输入一个主题,AI视频生成只要3分钟 2026/10/2 13:26:52

Pixelle-Video 完整教程:输入一个主题,AI视频生成只要3分钟

Pixelle-Video 完整教程:输入一个主题,AI视频生成只要3分钟 【免费下载链接】Pixelle-Video 🚀 AI 全自动短视频引擎 | AI Fully Automated Short Video Engine 项目地址: https://gitcode.com/GitHub_Trending/pi/Pixelle-Video Pixe…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉