1.offset 用不到索引,性能很差
| 偏移量 | 花费时间(秒 |
|---|---|
| 0 | 0.0010 |
| 10000 | 0.0190 |
| 4900000 | 4.9320 |
结论:偏移量越大,花费时间越多,多到形成无法忍受的慢查询
分析:数据库在执行limit语句的时候,当有偏移量的时候,需要从第一行开始一直遍历到偏移量的位置,没有用到索引,因为id可能有删除不能直接id直接加偏移量
2.返回字段也会影响效率
之所以花费这么多时间,除了和偏移量有关,还与返回字段有关,如果只返回索引字段,即覆盖索引查询,只需在索引字段扫描就可以了,所以查询时间大大缩短了,如下偏移4900000行的对比
select * from tableA offset 4900000 limit 20
select id from tableA offset 4900000 limit 20
| 返回字段 | 时间 |
|---|---|
| 所有 | 4.9320 |
| 主键id | 0.6120 |
3.利用索引where查询代替offset,大大提高效率
| sql | 时间 |
|---|---|
| select * from tableA where id >= (select id from tableA limit 4900000,1) limit 20 | 0.6780 |
| select id from tableA offset 4900000 limit 20 | 0.6120 |
| select * from tableA offset 4900000 limit 20 | 4.9320 |
结论:对比SQL1 和SQL2,利用where语句查询,即使返回全部字段,花费时间和只返回索引字段相差无几,第一个SQL的子查询其实就是第二个SQL,说明第一个SQL大部分时间都花费在了子查询上了
4.结合实际
where语句中用到了id,这个id从哪里得到呢?需要结合实际业务,由页面代码保留上一次的请求数据,下次请求时带上即可
请求下一页逻辑
直接返回上次返回结果集底部id,执行语句时where中id大于这个值即可
请求上一页逻辑
id因为删除的原因可能不是连续的,所以不能直接用id减去页数量取,需要先用上次结果集顶部id降序查找
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 数据库开销很大,高频使用会导致数据库压力增大
一些链表查询,数据库要保存中间查询结果,开销猛增,增加锁的竞争概率,中间过程临时表又不能有效使用索引,还会出现重复扫描现象,应该将链表查询拆分为多个单表查询,业务逻辑代码运算处理,再把结果呈现出来或者写入数据库