关于MySQL延迟连接优化中二级索引读取逻辑的疑问
关于MySQL延迟连接优化的两个疑问
假设表t的主键为pk,列x有唯一索引,表中包含其他若干列。原查询与优化后的延迟连接查询如下:
-- 简单查询 select * from t order by x limit 500, 20; -- 优化后的延迟连接查询 select * from t join ( select pk from t order by x limit 500, 20 ) dt using(pk);
原文提到:即使x列有唯一索引,简单查询中MySQL仍需读取500+20条完整行(并丢弃前500条),因为该二级索引仅包含x列和隐含在末尾的pk列。针对此结论有两个疑问:
- 作者是否指前500条数据的完整行都会被读取?
- 若确实如此,原因是什么?若仅读取第500条之后的20条,二级索引难道不能只读取这20条对应的pk吗?
问题1解答
是的,作者的表述完全准确:前500条数据的完整行确实会被MySQL读取出来,之后直接丢弃,仅保留后面20条返回给请求方。
问题2解答
核心原因在于MySQL处理limit offset, rows的执行逻辑:
- 当执行
select * from t order by x limit 500,20时,MySQL会先利用x的唯一索引完成排序,这个二级索引里只存储x和pk的值。 - 但因为查询语句要求返回所有列(
*),MySQL必须获取完整的行数据。所以它会按照二级索引的顺序,依次取出前500+20条对应的pk,并立即通过pk去聚簇索引(主键索引)中回表查询完整行数据。 - 为什么不能直接跳过前500条,只取后面20条的
pk回表?因为limit 500,20的逻辑本质是“先获取520条数据,再截断前500条”。MySQL的执行器必须遍历到第500条的位置,才能确定后续要获取的20条数据的pk值。而在遍历过程中,由于查询要求返回所有列,它无法只停留在二级索引层面,必须每遍历一条就回表取出完整行,直到凑够520条数据后再做截断处理。
而优化后的延迟连接查询,先通过子查询仅获取pk:子查询只需遍历x的二级索引,拿到520个pk后丢弃前500个,仅保留20个有效pk,再用这20个pk去回表查询完整行。这样就把回表次数从520次降到了20次,大幅减少了IO开销,这也是延迟连接优化的核心逻辑。
内容的提问来源于stack exchange,提问作者MeYokYang
相关产品推荐
相关产品推荐

