最近公司的有个业务功能在线上出现了问题,打开特别缓慢。且经常出现内存不够的情况,经过测试反馈,测试的数据量量大概是100万,且每条记录的数据量比较大。首先查询出第一页后,后调整中间页码或者最后段页码时出现上诉异常。
在查看sql语句后,使用的分页为offset+limit联合使用。对于这一联合使用,我们都知道,比如 offset=100 limit 50,表示取出的数据最终返回结果从第100至150记录,但首先查出的内容是0~150条记录后在进行最后结果的筛选。从而导致如果已知结果返回的大数据量时,需要先查出大量无用的数据,导致处理的数据量增大和内存异常。
解决该问题的方案如下:
1.使用索引+子查询进行优化
先根据条件获取对应的索引(建议唯一索引),在通索引获取最终返回的数据。
优点:因为先查询出索引,会使得返回的数据量小,占用的内存小。
缺点:任何优先查询无用的索引在进行筛选,仅解决因内存占用大的因素。
适用场景:平台列表按页展示时,调页的问题
2.开始位置的重定义
记录上一次记录的最后一条记录标记值(自增ID、时间戳或其他递增数字类型)字段,在下一次查询时加上此条件,降低查询数据的开始位置
优点:若调用为一页页的进行查询,可以把每一次查询左右首页处理。不管执行到多少页,执行的时间不变。
缺点:若调用为页码进行无序跳转时,上述问题仍会出现。
使用场景:APP、小程序等下拉列表展示。基本不会出现查询页无序跳转情况
3.主键限定优化
若主键ID为自增字段,可直接使用ID限定查询记录,如获取limit=50, offset=100,则转换为 id between 100 and (100+50) 返回对应的记录,若主键ID中间有过缺失的情况下,可以添加临时表配合使用,若临时表存储分页所用的ID,先查询临时表的ID在查询实际表中的记录。
4.降级、限制策略
若当前记录为100W,此时客户查询未尾段页(假如:500页码以上)时,直接给出提示。加强数据获取的标识,降低应当返回的记录。比如添加时间段或状态等特征降低数据量进行解决。
5.MyBatis-Plus/MyBatis 流式查询
流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果。流式查询的好处是能够降低内存使用。
MyBatis 提供了一个叫 org.apache.ibatis.cursor.Cursor 的接口类用于流式查询
注意点:使用流水查询后需要手动关闭与数据库的连接,否则容易出事!!!