MySQL「深度limit分页」优化

138 阅读2分钟

话不多说,直接从案例说明

案例一:覆盖索引+子查询优化

-- 分页查询用户信息,其中update_date为普通索引
select id, name, age from t_user where update_date > '2023-07-01' limit 1000000,10;

为什么慢?

在走update_date索引的前提下,该SQL执行流程:

  1. 走update_date索引查询符合条件的1000010条数据,这里只能获取到id;
  2. 走聚簇索引(回表)获取id,name,age字段;
  3. 返回1000010条数据到server层,根据offset的值挨个抛弃,然后只获取第1000001~1000010这10条数据

总结:

  1. 分页的深度越深,需要获取到server层的数据量就更多,只拿到符合条件的10条,但是需要获取100w零10条数据

如何优化?

先看优化后的SQL:

-- 覆盖索引+子查询优化
select id, name, age from t_user 
where id >= (
  select u.id from t_user u 
  where u.update_date > '2023-07-01'
  limit 1000000,1
)
limit 0, 10;

优化后SQL的执行看流程:

  1. 走update_date索引查询符合条件的1000010条数据,这里只能获取到id;
  2. 返回这1000010个id到server层进行过滤,获取到最终的1个id;
  3. 走主键索引获取10条数据,这里有完整的数据(包括id, name, age)

总结:

  1. 其实区别在于返回server层过程中,只会拷贝行内的id这一列的数据,不会拷贝整行数据,在数据量大的时候,这部分耗时也是非常明显的;
  2. 少了大数据回表的步骤,优化后只需要根据id查询size的数据返回即可

案例二:游标方式优化

-- 案例
select id, name, age from t_user order by id desc limit 1000000,10;

-- 游标方式优化后
select id, name, age from t_user where id >= 1000000 order by id desc limit 10;

这里的执行流程就不说了,总结:

  1. offset偏移量可以由前端传给后端,后端以防万一做数据合法性校验如offset最大值控制即可