回表为什么慢:二级索引到聚簇索引、覆盖索引与“延迟关联”
目标你能把“回表”解释成一个可量化的成本模型并掌握两类实战优化覆盖索引与延迟关联先查主键再回表。1. 先把概念说透InnoDB 的两棵树聚簇索引主键 B 树叶子存整行数据二级索引普通/联合索引 B 树叶子通常存索引列主键值作为指向聚簇索引的“地址”因此用二级索引定位到一批主键后还要到主键树再查一遍拿整行这个“再查一遍”就是回表。2. 回表慢在哪里随机 IO 缓冲池命中下降如果命中的主键分布很散每个主键都可能落在不同的数据页就会产生大量随机访问当结果集大时回表成本近似于回表次数 * (一次主键树查找的页访问)即使这些页最终在 buffer pool 命中CPU 也会因大量指针跳转和缓存失效而变慢。3. 如何识别“回表过多”常见信号EXPLAINkey是二级索引Extra没有Using indexSQL 表现where 过滤很强但 select 返回大量列select *limit 很大或范围很大3.1 一个可复现的最小例子同样的 where不同的 select 会差很多准备一张典型业务表createtablet_order(idbigintprimarykey,user_idbigintnotnull,create_timedatetimenotnull,titlevarchar(64)notnull,contentvarchar(2000)notnull,keyidx_user_time(user_id,create_time,id,title));这里故意放一个比较“大”的列content模拟详情字段。对照 1select *无法覆盖 - 必然回表explainselect*fromt_orderwhereuser_id1orderbycreate_timedesc,iddesclimit20;你应该预期key命中idx_user_time但Extra通常不会出现Using index因为content不在索引里必须回表。对照 2只查列表字段覆盖索引 - 少回表甚至不回表explainselectid,title,create_timefromt_orderwhereuser_id1orderbycreate_timedesc,iddesclimit20;你应该预期更可能出现Extra: Using index同样的 where/order但整体 IO 压力明显更小4. 第一类优化覆盖索引最优雅覆盖索引查询所需列都在二级索引叶子中不需要回表示例-- 列表页只要 id、create_timeselectid,create_timefromtwhereuser_id?orderbycreate_timedesclimit20;索引(user_id, create_time, id)验证Extra: Using index4.1 覆盖索引的边界索引太宽会降低扇出让树变高只把“高频查询必须返回的列”放进去即可5. 第二类优化延迟关联先少量主键再回表场景你必须返回很多列无法覆盖但你只需要返回很少行比如第一页 20 条典型写法select*fromtwhereidin(selectidfromtwhereuser_id?orderbycreate_timedesclimit20);直觉内层子查询只走二级索引拿到 20 个 id外层回表只回 20 次相比“先扫描很多索引再回表很多次”回表次数被压缩到limit。5.2 对照组大分页时延迟关联的价值最大错误回表被 offset 放大select*fromt_orderwhereuser_id1orderbycreate_timedesc,iddesclimit10000,20;正确先拿 20 个主键再回表 20 次select*fromt_orderwhereidin(selectidfromt_orderwhereuser_id1orderbycreate_timedesc,iddesclimit10000,20);注意这不是银弹仍然要EXPLAIN验证优化器是否按你的预期执行。5.1 注意点in (subquery)可能被优化器改写务必EXPLAIN验证如果排序字段与索引不一致仍会 filesort6. 与分页的关系大 offset 会放大回表select*fromtwhereuser_id?orderbycreate_timedesclimit100000,20;问题需要跳过 10 万行如果还要回表会产生大量无效回表建议seek 分页基于 lastId/lastTime7. 常见坑“加了索引还是慢”索引只是让定位更快但回表仍可能是瓶颈“Using index 就一定快”覆盖索引也可能扫很多行选择性差仍然慢8. 线上排查 checklistEXPLAIN 看Extra是否Using index检查是否select *检查是否大分页 offset检查索引是否能同时支持 where order8.1 更流程化的排查顺序建议照着做固定证据拿到慢 SQL 参数慢日志/APM不要只看模板先判断是否“回表导致的慢”where 很强但仍慢并且select *或返回列很多用 EXPLAIN 只看两件事key是否命中二级索引Extra是否缺少Using index意味着回表选择优化路径能只返回必要列做覆盖索引必须返回整行且 limit 小用延迟关联页码很大优先改 seek 分页否则 offset 本身就是瓶颈9. 面试背诵稿50 秒在 InnoDB 中主键是聚簇索引叶子存整行二级索引叶子存索引列加主键所以通过二级索引查整行通常要再去主键树查一次这就是回表。回表慢的本质是大量随机访问数据页尤其是结果集大或分页 offset 大时会放大。优化主要两类第一是覆盖索引让查询列都在二级索引叶子里EXPLAIN 的 Extra 会出现 Using index第二是延迟关联先用索引拿到少量主键比如 limit 20再回表取整行从而把回表次数压缩到分页大小。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2479566.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!