深分页太慢如何优化?
当进行分页的时候,如果跳到一个很深的页码,会执行这样的一条sql语句。结合sql的底层执行流程讲一下为什么这么慢?需要Java高频面试资料,需要的小伙伴可以看主页的置顶视频,免费的进行领取。
·当执行这条sql语句的时候会来到Mysql的服务端,首先会经过server层进行语法解析,解析这条sql的语法是不是正确,然后进行预处理,看表是不是存在,字段是不是存在,再优化sql。
·接着会来到引擎层去找数据,这里注意并不是直接定位到第5万条,而是从第0条找到第50100条,所以慢就慢在这里。但如果页分的越深,拿的数据就越多。拿到这一堆数据之后会返回到sort,再根据limit以及条件,还有分组等筛选到最终的100条数据。
·当然这还不是最慢的,如果sql语句当中涉及到二级索引,并且查询字段没有用到索引覆盖,再结合深分页会雪上加霜,更加的慢。假设为age字段创建了一个二级索引,二级索引的b+树上面的叶子节点就会存储age和主键ID这两个数据。所以当查询的字段涉及到id和age之外的,此时就会进行回表。
底层的执行流程可是这样的:
·首先会去引擎层拿到二级索引age>1的从第0条到50100条的数据,然后返回到server。
·由于查询字段涉及到ID和age之外的,就需要再来到一级索引的b加数,也就是主键索引的b加数。因为所有的数据都在那颗主键索引上面,也可以叫做一级索引。这种过程称之为回表,所以涉及到回表的性能会进一步下降。当为这50100条数据都回完表之后,它会最终再去筛选其中的第50100条,所以这个性能会更慢。
·优化深分页-二级索引。来说一下怎么进行优化。首先毫无疑问就要将字段改成索引覆盖。当然假如非要查询二级索引以外的数据,比如再查一个name,这个时候肯定又会涉及到回表。可以这样来解决,把查询作为一个子查询,在子查询当中进行索引覆盖,再将这100条索引覆盖的数据去一级索引当中进行查询。
这样是可以解决深份页下面二级索引查询慢的问题的。当然此时其实深分页依然没有解决,依然会从第0条找到第520100条。这个地方可以这样来解决,将ID作为页码进行定位,通过ID可以直接通过b+数的索引检索定位到第5万条,然后再拿到那100条数据,这样的性能无疑是最佳的。
但是使用这种方式肯定也有一些弊端,比如中间删掉了一些数据,有可能就会造成某一些页数达不到满页。比如这里的100条,比如某页删掉了两条数据,有可能查询到的数据就是98条。如果业务允许的情况下,通过这种方式改善肯定是比较好的。
当然还有一个前提是ID必须要是自增的主见,如果是一些UUID肯定也没法用。在这个基础上还可以用另外一种解决方案,也就是游标分页。当从第一页跳到第二页,可以拿到上一页最大的ID,这样就可以定位到第二页所需要的ID起始值。这种方式同样的也是将ID作为页码来进行分页。这种方式同样的也有一个弊端,就是分页功能需要禁止掉跨页,只能一页一页的翻。因为跨页要拿上一页最大的ID就比较麻烦了,并且同样的UUID这种非自增的主键也没有办法进行实现。
所以在这里我还提供另外两种解决方案。
·第一种是分估分表,也就是按照数据范围来进行分表,比如第一条到100万一个表,第100万到200万一个表。这样如果要身份页到第200万条数据,只需要去查询第二个表即可。
·另外还可以使用ES大数据,这种凡是数据量大、性能低的万金油解决方案。
当然在考虑成本的情况下,其实还可以去修改业务。业务如果允许的情况下可以限制深分,比如像淘宝、京东,他们的商品最多就只翻到第100页,还想往后面翻会进行禁止掉。所以在很多情况下,如果在业务允许的情况下,其实只需要对业务做一个小小的调整,其实就可以节省掉使用很大的成本去实现各种各样复杂的技术。
所以在做set优化的时候,大家也一定要考虑业务层面的优化。
我就给大家讲到这里,如果视频对你有帮助,可以给徐老师一键三连,我们下期见。
本文暂时没有评论,来添加一个吧(●'◡'●)