MySQL 中的回表是什么?
- 标签:后端、MySQL、数据库
🏛 业务架构师
回表指:查询走了二级索引,但索引里没包含所需的全部列,于是拿二级索引叶子节点里的主键值,再去聚簇索引上查一次完整数据行。两次查找,第二次通常是随机 IO,代价不小。
我用业务账算这笔成本:订单表 5000 万行,查询"某用户最近订单"走了 (user_id, created_at) 索引,命中 50 条就要回表 50 次;如果列表页只展示订单号、状态、金额、时间这几个字段,把 select * 改为明确列清单,并把高频展示列做进覆盖索引,50 次随机 IO 直接归零。在数据库机器接近容量水位时,这类改造推迟了扩容采购。
治理上我落三条规矩:
- **列表接口禁止 select ***:只取展示所需列,既省回表也省网络带宽,这是高杠杆、零风险的改造;
- 判断是否需要覆盖索引:对 QPS 高的列表查询评估加列进联合索引,用 EXPLAIN 确认 Extra 出现 Using index;
- 别为了消灭回表而堆索引:回表是"读的成本",索引是"写的成本",按读写比和业务高峰权衡。
回表的意义在于让团队理解:索引优化的本质是减少随机 IO,理解了它,慢查询治理就从碰运气变成可推算的工程。
🏗 系统架构师
回表指通过二级索引找到主键值后,再回到聚簇索引里取完整行数据的过程。二级索引叶子节点只存"索引列 + 主键",不存整行,所以只要查询还需要索引之外的列,就必须回表。
代价分析:回表走的是主键随机 IO。二级索引范围扫描 1000 条,就可能有 1000 次随机回表,每次 3~4 个页访问,这往往就是"explain 显示用了索引但 SQL 依然很慢"的元凶——type=index 或 range 但 Extra 没有 Using index,说明大量回表在发生。
我的治理手段:
- 设计覆盖索引,让热点查询止步于二级索引;
- 延迟关联:
select t.* from t join (select id from t where x limit 100000, 20) tmp on t.id = tmp.id,先用覆盖索引拿主键,再精确回表 20 次,深度分页场景立竿见影; - 判断要不要治理看回表量级:几十上百次可接受,上万次必须优化。
💻 全栈开发专家
回表 = 拿二级索引查到的主键值,再去聚簇索引(主键 B+ 树)查一次完整行数据。因为二级索引叶子节点只存索引列 + 主键,不含其他列。
sql
-- 索引 idx_name(name)
SELECT id, name, age FROM user WHERE name = '张三';
-- 1) idx_name 树上找到 '张三' → 拿到 id=100(索引层)
-- 2) 聚簇索引树上按 id=100 再查一遍拿 age(回表,随机 IO)为什么这是性能关键:二级索引上多是顺序扫,而回表是按主键随机读,如果命中 1 万行就回表 1 万次,优化器可能直接放弃索引走全表扫(EXPLAIN 里明明建了索引却是 type=ALL 的常见原因)。
我减少回表的三个手段:
- 覆盖索引:把查询列加进联合索引,Extra 变
Using index。 - 延迟关联:先在索引里筛出主键再 join 回原表,把回表限制在最终分页的那几行。
- 只查必要的列,坚决消灭
SELECT *。