文章目录
- 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卡死);二是,数据查询非常慢,用户等待时间过长,用户体验非常差,而且还可能造成前端请求超时。