用户大量导出数据,发现数据总量是对的,但是有大量重复数据,且重复的有规律。
分区表,id增序,范围分区(less than),分区字段date,保留三十个分区的数据。
使用了PageHelper
优化思路:使用拦截器,拦截limit sql进行改写
原sql:
select * from table_name where filed1 = '' and filed2 = '' and date >= 'startDate' and date <= 'endDate' limit offset,10000
优化sql:
select * from table_name where filed1 = '' and filed2 = '' and date >= 'startDate' and date <= 'endDate' and id >= (select id from table_name where filed1 = '' and filed2 = '' and date >= 'startDate' and date <= 'endDate' order by id limit offset, 1) limit 10000
通过子查询获取每次分页的第一条记录的id,然后往后查询10000条记录
利用的是覆盖索引的方式,减少了回表的性能损耗。
在公司私有云查询每次查询的数据的id是一样的,这样就证明了他的想法,他的解释是偏移量始终是B,也就是1(不变),每次取A条记录,往后取10000,不管你的A如何变化,最后每次取的都是第一条记录的id。
测试发现,公司私有云不支持偏移量,每次返回的第一个id就是一样的,这也是为什么会有后面的2、3、4、5、6.....
关于limit有两种用法:
a、limit A, B
b、limit B offset A
A:表示偏移量
B:表示pageSize
测试环境百万数据导出,没有重复
生产执行explain,索引没有问题
查看开发环境、测试环境、生成环境的mysql版本,以及缓存的参数,是一致的
查看mybatis源码,二级缓存没有开启,一级缓存计算cacheKey使用了参数这一部分作为生成条件,排除
导出的数据没有重复,且执行计划没有问题
最后发现8月3号的数据的max id比8月1号的数据的max id要小,上游在推送数据的时候,采用的方案是每天清空数据,全量推送,不保证分区顺序,8月3号的数据推送的时间比8月1号要早,造成了分区内部id有序,整体分区id无序。
当然,还发现了一个其他的问题,也就是我们只保留30个分区,也就是30天的数据,使用范围分区(less than),没有命中的分区数据,默认归到第一个分区,也就是date小于第一个分区日期的数据,全部插入到第一个分区,造成第一个分区几千万数据,其他分区几十万数据。
首先会通过子查询确认是哪个分区,然后在满足其他条件的情况下,从排序结果中获取第一条记录的id(正确),然后执行外查询,也是确认分区,从id开始的位置查询满足条件的记录,每次取一万条数据,但是当跨分区查询时,id变的无序的时候,那么就可能每次取到的数据是重复的。
可以将每次分页的下一个id传进来,这样就不用子查询了
注:
当然也可以,在外层再加order by id,但是会有什么问题呢?
子查询对所有分区进行了一次排序,外查询又进行了一次排序,性能会受影响。