文章目录

  • 1、问题背景
  • 2、问题描述
  • 3、解决方案
  • 4、总结


1、问题背景

(1)待查询表中的数据超过4000万条;
(2)已经对sql的进行过优化,对表结构等进行优化,并且已经对sql中的查询条件都添加了索引;
(3)前端分页需要的数据格式如下:

{
    "total": 0,
    "data": []
}

2、问题描述

  • 在进行分页时,由于前端需要知道总的条数(total)。这里有两个普遍的解决办法:一是,在查询数据时,只执行一个sql,相当于查询所有,然后在程序中进行分页;二是执行两个sql,第一次查询分页的数据,第二次执行count()操作。
  • 但是无论使用哪种方式,对于前端查询而言都会特别慢。

3、解决方案

(1)方法一:

  • 不告诉前端总的数据量,不传total值。让用户一直点击“下一页”,当点击后返回数据为空,即表示已经到了最后一页。此时按钮变为不可用状态。这种做法的好处是,只执行一个分页的sql,效率比较高。
    如下:
{
    "data": []
}

(2)方法2:

  • 但是为了不影响(或者降低)用户体验,增加一个参数,“isStill,表示是否还有下一页”。该参数为true表示有,false表示无。此时,后端需要执行两个分页的sql,每次查询都多执行后一次的分页。这种效率比count()的效率要高很多。
{
    isStill:true,
    "data": []
}

(3)方法3:

  • 把查询总条数和查询源数据分为两个接口。每次查询时,只是分页的第一页需要同时查两个接口。并且当查询条件改变时,需要同时查两个接口。由前端判断查询条件是否改变了。注意,在查询总条数时,务必对sql进行简化,越简单越好。
    (该方案,其实还有一种实现,就是由后端对总条数进行缓存(可以放内存/redis中),由后端判断查询条件是否改变。但是我建议使用前端判断的方法)

4、总结


  • 方法一:
    缺点是,用户不知道下一页是否还有数据,只有当点击了下一页按钮后才会知道;
    优点是,效率高;
  • 方法二:
    缺点是,多执行了一个分页的sql,效率较低;
    优点是,用户体验好一些;
  • 方法三:
    个人推荐!

在执行count()查总条数的sql时,使用最简单的sql进行(比如,去掉排序,去掉多余的表联合,使用count(1)代替count(*)等);
但是,如果连第一次count()都慢的查不出来时,那就没办法了,要不做分表分库,要不使用方法二。


慢查询一是,会严重降低数据库性能,如果有多个应用程序都需要操作数据库的话,会导致所有应用都无法使用(我在生产环境就遇到了这个问题,非常严重的问题,生产环境中整个系统无法使用,原因就是mysql卡死);二是,数据查询非常慢,用户等待时间过长,用户体验非常差,而且还可能造成前端请求超时。