数据库深分页问题优化

182 阅读3分钟

参考juejin.cn/post/701201…

1.offset 用不到索引,性能很差
偏移量花费时间(秒
00.0010
100000.0190
49000004.9320

image.png

image.png

image.png

结论:偏移量越大,花费时间越多,多到形成无法忍受的慢查询

分析:数据库在执行limit语句的时候,当有偏移量的时候,需要从第一行开始一直遍历到偏移量的位置,没有用到索引,因为id可能有删除不能直接id直接加偏移量

2.返回字段也会影响效率

之所以花费这么多时间,除了和偏移量有关,还与返回字段有关,如果只返回索引字段,即覆盖索引查询,只需在索引字段扫描就可以了,所以查询时间大大缩短了,如下偏移4900000行的对比

 select * from tableA offset 4900000 limit 20
 select id from tableA offset 4900000 limit 20
返回字段时间
所有4.9320
主键id0.6120
3.利用索引where查询代替offset,大大提高效率

image.png

sql时间
select * from tableA where id >= (select id from tableA limit 4900000,1) limit 200.6780
select id from tableA offset 4900000 limit 200.6120
select * from tableA offset 4900000 limit 204.9320

结论:对比SQL1 和SQL2,利用where语句查询,即使返回全部字段,花费时间和只返回索引字段相差无几,第一个SQL的子查询其实就是第二个SQL,说明第一个SQL大部分时间都花费在了子查询上了

4.结合实际

where语句中用到了id,这个id从哪里得到呢?需要结合实际业务,由页面代码保留上一次的请求数据,下次请求时带上即可
请求下一页逻辑
直接返回上次返回结果集底部id,执行语句时where中id大于这个值即可

image.png

请求上一页逻辑
id因为删除的原因可能不是连续的,所以不能直接用id减去页数量取,需要先用上次结果集顶部id降序查找 image.png

select id from (select id from tableA where id < 4900000 order by id desc limit 20 ) as tmp order by id limit 1

子查询获取上一页的所有id,然后外层查询返回最小的id,两个查询都用到了索引,花费 0.0010秒

找到上一页的顶部id,就可以正序查询了,获取上一页数据的SQL最终为

select * from tableA where id > (select id from (select id from tableA where id < 4900000 order by id desc limit 20 ) as tmp order by id limit 1) limit 20

疑问:能直接倒序获取上一页数据,然后代码里面翻转顺序吗

select * from tableA where id < 4900000 order by id desc limit 20
5.总结

count sum groupby 性能不友好,sum groupby 数据库开销很大,高频使用会导致数据库压力增大

一些链表查询,数据库要保存中间查询结果,开销猛增,增加锁的竞争概率,中间过程临时表又不能有效使用索引,还会出现重复扫描现象,应该将链表查询拆分为多个单表查询,业务逻辑代码运算处理,再把结果呈现出来或者写入数据库

image.png