行驶数据查询和导出优化:Ymatrix查询一天数据会很快在1秒内,区间查询时间超过7天效率会下降到7秒+。
所以在区间日期查询行驶数据时做优化,将区间日期拆分为一天去查ymatrix提升查询效率
如查询条件 2023-11-28 16:41:11 - 2023-12-05 16:41:11,pageNum=1,pageSize=10
开始时间 结束时间 总数 offset pageSize 执行时间拆分如下? 、 并发查询每一条时间总条数据(查询效率500ms以内) startTime='2023-12-04 16:41:11', endTime='2023-12-05 16:41:11' , initCount=4 startTime='2023-12-03 16:41:11', endTime='2023-12-04 16:41:11' , initCount=23 startTime='2023-12-02 16:41:11', endTime='2023-12-03 16:41:11' , initCount=18 startTime='2023-12-01 16:41:11', endTime='2023-12-02 16:41:11' , initCount=20 startTime='2023-11-30 16:41:11', endTime='2023-12-04 16:41:11' , initCount=10 startTime='2023-11-29 16:41:11', endTime='2023-11-30 16:41:11' , initCount=5 startTime='2023-11-28 16:41:11', endTime='2023-11-29 16:41:11' , initCount=1 计算页码归属日期条件 i=1, startTime='2023-12-04 16:41:11', endTime='2023-12-05 16:41:11', initCount=4, count=4, offset=0, pageSize=4 startTime='2023-12-03 16:41:11', endTime='2023-12-04 16:41:11', initCount=23, count=17, offset=0, pageSize=6 i=2, startTime='2023-12-03 16:41:11', endTime='2023-12-04 16:41:11', initCount=23, count=17, offset=6, pageSize=10 i=3, startTime='2023-12-03 16:41:11', endTime='2023-12-04 16:41:11', initCount=23, count=7, offset=16, pageSize=7 startTime='2023-12-02 16:41:11', endTime='2023-12-03 16:41:11', initCount=18, count=15, offset=0, pageSize=3 i=4, startTime='2023-12-02 16:41:11', endTime='2023-12-03 16:41:11', initCount=18, count=15, offset=3, pageSize=10 i=5, startTime='2023-12-02 16:41:11', endTime='2023-12-03 16:41:11', initCount=18, count=5, offset=13, pageSize=5 startTime='2023-12-01 16:41:11', endTime='2023-12-02 16:41:11', initCount=20, count=15, offset=0, pageSize=5 i=6, startTime='2023-12-01 16:41:11', endTime='2023-12-02 16:41:11', initCount=20, count=15, offset=5, pageSize=10 i=7, startTime='2023-12-01 16:41:11', endTime='2023-12-02 16:41:11', initCount=20, count=5, offset=15, pageSize=5 startTime='2023-11-30 16:41:11', endTime='2023-12-01 16:41:11', initCount=10, count=5, offset=0, pageSize=5 i=8, startTime='2023-11-30 16:41:11', endTime='2023-12-01 16:41:11', initCount=10, count=5, offset=5, pageSize=5 startTime='2023-11-29 16:41:11', endTime='2023-11-30 16:41:11', initCount=5, count=5, offset=0, pageSize=5 i=9, startTime='2023-11-28 16:41:11', endTime='2023-11-29 16:41:11', initCount=1, count=1, offset=0, pageSize=1
public class LimitCache {
private String startTime;
private String endTime;
//剩余条数
private int count;
//总条数
private int initCount;
//查询数据库的起始数据
private int offset;
//页数
private int pageSize;
//第几页
private int index;
public void setInitCount(int initCount) {
this.initCount = initCount;
}
public void setCount(int count) {
this.count = count;
if (this.initCount == 0){
this.initCount = count;
}
if (initCount <= count ){
this.initCount = count;
}
}
}
public class QueryVehicleDataByVinListDomain {
private String vin;
private String startTime;
private String endTime;
private Integer pageNo;
private Integer pageSize;
private AtomicInteger atomicInteger = new AtomicInteger();
private ThreadPoolExecutor poolExecutor = SweetThreadPoolExecutor.getExecutor(5, 1000);
private Cache<String, Integer> countCache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build();
public QueryVehicleDataByVinListDomain(String vin, String startTime, String endTime, Integer pageNo, Integer pageSize) {
this.vin = vin;
this.startTime = startTime;
this.endTime = endTime;
this.pageNo = pageNo;
this.pageSize = pageSize;
atomicInteger.getAndIncrement();
}
public PageInfo<NewEnergyVehicleDataDto> handle() {
List<LimitCache> tcs = queryTotal(time(startTime, endTime));
int pages = (int) Math.ceil((double) (tcs.stream().map(LimitCache::getCount).reduce(0, Integer::sum)) / pageSize);
int count = tcs.stream().map(LimitCache::getCount).reduce(0, Integer::sum);
NewEnergyVehicleDataMapper newEnergyVehicleDataMapper = SpringApplicationUtils.getBean(NewEnergyVehicleDataMapper.class);
PageInfo<NewEnergyVehicleDataDto> retPage = new PageInfo<>();
if (count > 0) {
List<List<LimitCache>> lists = doGetList(tcs);
List<NewEnergyVehicleDataEntity> list = Lists.newArrayList();
List<LimitCache> cacheList = lists.get(pageNo - 1);
log.info("执行分页查询 list.index【{}】,pageNo【{}】,LimitCache 【{}】 ", pageNo - 1, pageNo, cacheList.toString());
cacheList.stream().forEach(v -> {
List<NewEnergyVehicleDataEntity> list2 = newEnergyVehicleDataMapper.selectNewEnergyVehicleDataList(v.getStartTime(), v.getEndTime(), Arrays.asList(vin), v.getOffset(), v.getPageSize());
list.addAll(list2);
});
retPage.setList(new ConvertNewEnergyVehicleDomain(list).handle());
retPage.setTotal(count);
retPage.setPages(pages);
retPage.setPageNum(pageNo);
retPage.setPageSize(pageSize);
}
return retPage;
}
//缓存
private List<List<LimitCache>> doGetList(List<LimitCache> tcs) {
return handleTimeToPages(tcs, pageSize);
}
private List<List<LimitCache>> handleTimeToPages(List<LimitCache> list, int pageSize) {
List<List<LimitCache>> ret = Lists.newArrayList();
List<LimitCache> newList = Lists.newArrayList();
for (LimitCache lc : list) {
LimitCache l = new LimitCache();
BeanUtils.copyProperties(lc, l);
newList.add(l);
}
int index = 1;
int offset = 0;
while (CollectionUtils.isNotEmpty(newList) && index <= list.size()) {
List<List<LimitCache>> limitCaches = doHandleTimeToPages(newList, pageSize, offset);
ret.addAll(limitCaches);
//获取最后一条记录取 offset
LimitCache limitCache = limitCaches.get(limitCaches.size() - 1).get(limitCaches.get(limitCaches.size() - 1).size() - 1);
//时间上一页还有剩余,则是需要记录offset
if (limitCache.getCount() != 0 && (limitCache.getOffset() + limitCache.getPageSize()) < limitCache.getInitCount()) {
offset = limitCache.getPageSize();
} else {
offset = 0;
}
index++;
}
return ret;
}
private List<List<LimitCache>> doHandleTimeToPages(List<LimitCache> list, int pageSize, int offset) {
Iterator<LimitCache> iterator = list.iterator();
List<List<LimitCache>> ret = Lists.newArrayList();
while (iterator.hasNext()) {
LimitCache limitCache = iterator.next();
int count = limitCache.getCount();
//如果当前行不满一页
if (count < pageSize) {
int index = atomicInteger.getAndIncrement();
List<LimitCache> limitCaches = Lists.newArrayList();
limitCache.setPageSize(count);
limitCache.setIndex(index);
limitCache.setOffset(offset);
limitCaches.add(limitCache);
iterator.remove();
limitCaches.addAll(getLimitCaches(list, pageSize, count, index));
ret.add(limitCaches);
return ret;
//当前行大于1页
} else {
iterator.remove();
int pages = (int) Math.ceil((double) limitCache.getCount() / pageSize);
for (int i = 1; i <= pages; i++) {
int index = atomicInteger.getAndIncrement();
List<LimitCache> limitCaches = Lists.newArrayList();
int cnt = limitCache.getCount();
LimitCache lc = new LimitCache();
BeanUtils.copyProperties(limitCache, lc);
int os = ((i - 1) * pageSize) + offset;
lc.setOffset(os);
//不满足则需要借
if (cnt < pageSize) {
lc.setPageSize(cnt);
lc.setIndex(index);
limitCaches.add(lc);
limitCaches.addAll(getLimitCaches(list, pageSize, cnt, index));
ret.add(limitCaches);
} else {
lc.setIndex(index);
lc.setPageSize(pageSize);
limitCaches.add(lc);
ret.add(limitCaches);
}
limitCache.setCount(limitCache.getCount() - pageSize);
}
return ret;
}
}
return ret;
}
/**
* 不满足pageSize则一直取
*
* @param list
* @param pageSize
* @param count
* @param index
* @return
*/
private List<LimitCache> getLimitCaches(List<LimitCache> list, int pageSize, int count, int index) {
List<LimitCache> ret = Lists.newArrayList();
Iterator<LimitCache> iterator = list.iterator();
int count2 = count;
while (iterator.hasNext()) {
LimitCache limitCache = iterator.next();
count2 = limitCache.getCount() + count2;
//如果当前行不满一页
if (count2 < pageSize) {
limitCache.setPageSize(limitCache.getCount());
limitCache.setOffset(0);
limitCache.setIndex(index);
ret.add(limitCache);
iterator.remove();
//刚好满足pageSize
} else if (count2 == pageSize) {
//余数
limitCache.setPageSize(limitCache.getCount());
limitCache.setOffset(0);
limitCache.setIndex(index);
iterator.remove();
ret.add(limitCache);
break;
//满足超出,则借用下一个日期
} else {
//需要借用多少
int c = (pageSize - (count2 - limitCache.getCount()));
limitCache.setPageSize(c);
limitCache.setCount(limitCache.getCount() - c);
limitCache.setOffset(0);
limitCache.setIndex(index);
LimitCache lc = new LimitCache();
BeanUtils.copyProperties(limitCache, lc);
ret.add(lc);
break;
}
}
return ret;
}
private List<LimitCache> queryTotal(List<LimitCache> list) {
List<LimitCache> ret = Lists.newArrayList();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
RequestContextHolder.setRequestAttributes(servletRequestAttributes, true);//设置子线程共享
//这里创建线程池查询没有java8list多线程,是因为java8创建的线程数太少影响效率
NewEnergyVehicleDataMapper newEnergyVehicleDataMapper = SpringApplicationUtils.getBean(NewEnergyVehicleDataMapper.class);
List<Future<Map<String, Integer>>> futures = Lists.newArrayList();
int i = 0;
for (LimitCache lc : list){
if (i == 0){
//最新日期总数会变化,所以不做缓存每次都查最新
poolExecutor.submit(() -> {
Integer count = newEnergyVehicleDataMapper.selectNewEnergyVehicleDataCount(lc.getStartTime(), lc.getEndTime(), Arrays.asList(vin));
lc.setCount(count);
});
i++;
continue;
}
Future<Map<String, Integer>> futureMap = poolExecutor.submit(() -> {
String key = vin + lc.getStartTime() + lc.getEndTime();
Integer count = countCache.getIfPresent(key);
if (count == null) {
count = newEnergyVehicleDataMapper.selectNewEnergyVehicleDataCount(lc.getStartTime(), lc.getEndTime(), Arrays.asList(vin));
countCache.put(key, count);
}
Map<String, Integer> countMap = Maps.newLinkedHashMapWithExpectedSize(1);
countMap.put(lc.getStartTime() + lc.getEndTime(), count);
//查询数据库
lc.setCount(count);
return countMap;
});
futures.add(futureMap);
i++;
}
Map<String, Integer> countMap = Maps.newHashMap();
for (Future<Map<String, Integer>> future : futures) {
try {
countMap.putAll(future.get());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
for (LimitCache lc : list) {
LimitCache newLc = new LimitCache();
BeanUtils.copyProperties(lc, newLc);
Integer cnt = countMap.get(lc.getStartTime() + lc.getEndTime());
if (cnt != null){
newLc.setCount(cnt);
}
ret.add(newLc);
}
return ret;
// list.get(0).setCount(6);
// list.get(1).setCount(3);
// list.get(2).setCount(2);
// list.get(3).setCount(8);
// list.get(4).setCount(1);
// list.get(5).setCount(12);
// list.get(6).setCount(8);
// return list;
// Random random = new Random();
// List<LimitCache> ret = Lists.newArrayList();
// for (LimitCache lc : list){
// LimitCache newLc = new LimitCache();
// //查询数据库
// lc.setCount(random.nextInt(30));
// BeanUtils.copyProperties(lc,newLc);
// ret.add(newLc);
// }
// return ret;
}
/**
* 根据数据的排序规则
* 现在是降序,从大到小,
* 如果需要升序,则需要改下代码
* @param startTime
* @param endTime
* @return
*/
private List<LimitCache> time(String startTime, String endTime) {
DateTimeFormatter format = DateTimeFormatter.ofPattern(DateUtils.DATE_FORMAT_STRING);
List<LimitCache> times = Lists.newArrayList();
String sd = startTime.substring(0, 10);
String ed = endTime.substring(0, 10);
LocalDate startDate = LocalDate.parse(sd, format);
LocalDate endDate = LocalDate.parse(ed, format);
LimitCache slc = new LimitCache();
slc.setStartTime(ed + " 00:00:00");
slc.setEndTime(endTime);
times.add(slc);
long numOfDays = ChronoUnit.DAYS.between(startDate, endDate);
for (int i = 1; i < numOfDays; i++) {
LocalDate currentDate = endDate.minusDays(i);
LimitCache lc = new LimitCache();
lc.setStartTime(currentDate.format(format) + " 00:00:00");
lc.setEndTime(currentDate.format(format) + " 23:59:59");
times.add(lc);
}
LimitCache elc = new LimitCache();
elc.setStartTime(startTime);
elc.setEndTime(sd + " 23:59:59");
times.add(elc);
return times.stream().sorted(Comparator.comparing(LimitCache::getEndTime).reversed()).collect(Collectors.toList());
}