MySql 深度分页问题优化思路

user 表描述:id 为主键,在 name 上建立了索引,也有其他字段但都不重要

想要分页,首先想到的就是这样的语句

select * from user limit offset, size;
  • 问题来了,这种方式同样拿十条数据,第一页和第一百页的查询速度是不一样的

  • 当我们执行上面的 sql 取第一页的 10 条数据时,server 层会在存储引擎的主键索引中依次获取到第 0 到 10 条完整行数据,并放到 server 层的结果集中,返回给客户端

  • 当我们把页码调高,比如第 10w 页,server 会在存储引擎的主键索引中依次获取第 0 到 10w*10+10=1000010 条完整行数据,然后抛弃不需要的数据行,只留下最后的 10 条,放到 server 层的结果集中,返回给客户端

优化方式

  • 当页数非 0 时,server 层会获取到很多无用的数据,而当 select 后面是 * 时,需要获取完整的行信息,我们可以从这里入手,只获取一两个字段减少获取时间,所以我们可以这样优化 sql
-- 优化前
select * from user limit 1000000, 10

-- 优化后
select * from user where id >= (select id from user limit 1000000, 1) limit 10
  • 我们先用找到数据的起始位置,然后以该主键作为查询条件向后找 10 条数据
  • 这样就保证了 server 层在获取时只获取 id 这一列,而不是获取完整数据行,单行数据量大时这中优化是很明显的。测试来看能快一倍左右

非主键索引的 limit 优化过程

select * from user order by name limit 0, 10;
  • 上面 sql 的执行过程中,server 层首先会在存储引擎的 name 列索引获取到第 0 条数据,然后回表到主键索引中找到对应的完整行数据,反复依次找到 10 条数据后,放到 server 层的结果集中,返回给客户端
  • 当页数很大时,比如 10w ,就意味着 1000010 次的回表,并且全部获取到 server 层中再抛弃不需要的数据行
  • 当我们真这样获取第 10w 页的数据时,很大可能该 sql 的执行计划中 type 会变为 all ,也就是全表扫描。很明显,优化器在看到 100w+ 次的回表之后,摇了摇头,还不如一条条数据去判断算了,于是选择了全表扫描
  • 全表扫描显然是不行的,性能太差了,可通过下面 sql 进行优化
select * from user where id in (select id from user order by name limit 1000000, 10);
  • 这样 server 层在获取时避免了回表操作,避免了获取完整数据行,性能要高很多

总结

  • 其实这种深度分页问题在实现上只能通过一些手段缓解,此时我们应该考虑为什么代码会有深度分页问题,需求是怎样的,能不能规避深度分页?
  • 如果只是为了做一个展示页的功能,是不是应该考虑不应该翻到 10w 页之后,这明显不合理
  • 或者我们是不是可以考虑在实现形式上不支持跳页,只支持上一页和下一页,这样我们就可以通过记录上一次获取到的主键进行比较,避免深度分页问题。高大上一点就叫瀑布流
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值