经过测试,优化前的接口需要20多秒才能返回结果,优化后一般只需要5秒左右,如果查询频繁会变成500ms左右(因为有缓存)。
- 我的场景是,单个查询接口,涉及大量IO操作,无法减少IO的数量。
- 每个IO都是去数据库查询IO,不考虑优化sql,使用其它方法优化接口响应速度。
当查询大报表,并且sql没有太大优化空间的情况下,该如何提升效率呢?
- 也就是说现在IO太多,但是也没办法,业务确实需要这么多的IO
- 单个IO的效率优化,已经没有太多优化空间了,也就是优化sql的方案不可取
- 那么问题就是如何让这些IO能快一点
一般这种情况,就可以考虑多线程了,而且大的报表一般是不用考虑高并发的。所以直接将内容都写到方法中,不定义到类中,就没有线程安全问题。因为没有高并发,也不用考虑栈溢出的问题(每个线程执行都会有一个,线程私有,生命周期与线程相同,是Java方法执行的线程内存模型,每个方法被执行时,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息)
当然,用到多线程,就需要考虑很多线程同步问题,而且异步执行IO,如何最终整合结果,都是需要解决的问题
我采用的方案如下
- 使用submit()方法进行异步提交
- 使用Callable接口的call方法,不使用Runnable接口的run。习惯了,需要返回值的场景,我都统一用Callable,用其它的也行。
- Callable接口的call方法可以给我们一个Future类型的返回值(不是只有它可以给Future返回值),我们可以通过这个Future获取线程中代码的返回值
- 使用JUC工具类中的倒数门栓CountDownLatch来做线程同步
- 为了不重复的写线程代码,编写统一工具类,通过反射来进行代码的执行
想要实现的效果如下(我希望和原来直接调用mapper接口一样简单,并且还能将单线程同步IO,实现为多线程异步IO。)
- 没改之前的代码,直接调用mapper接口,但是只有执行完第一个接口,才能执行第二个,以此类推。
List<String> provinces = cmsReportMapper.querySaleProvince();
List<ConfirmationsDTO> confirmationsDTOS = cmsReportMapper.queryConfirmationsByMonth(currentYear);
List<ConfirmationsDTO> confirmationsDTOSTotal = cmsReportMapper.queryConfirmationsTotal(currentYear);
- 改之后的,可以发现,对于调用mapper几乎是一样的,只不过需要额外传输反射需要的东西和CountDownLatch倒数门栓对象。不过实现了异步,这些mapper将一起异步执行。
Future<Object> provincesFuture = statementThreadUtils.runOneIOLatchCountDown(cmsReportMapper, latch, "querySaleProvince", null);
Future<Object> confirmationsDTOSFuture = statementThreadUtils.runOneIOLatchCountDown(cmsReportMapper, latch, "queryConfirmationsByMonth", oneStringClass, currentYear);
Future<Object> confirmationsDTOSTotalFuture =statementThreadUtils.runOneIOLatchCountDown(cmsReportMapper,latch,"queryConfirmationsTotal",oneStringClass,currentYear);
- 完整使用的形式,可见实现起来还是很方便的。让所有IO都异步执行了。
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,808,10,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(50),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());
@Override
public List<ConfirmationsDTO> queryConfirmationsBySaleProvince(String currentYear) {
LinkedList<ConfirmationsDTO> result = new LinkedList<>();
try{
final CountDownLatch latch = new CountDownLatch(3);
StatementThreadUtils statementThreadUtils = new StatementThreadUtils(threadPoolExecutor);
Class[] oneStringClass = {String.class};
Future<Object> provincesFuture = statementThreadUtils.runOneIOLatchCountDown(cmsReportMapper, latch, "querySaleProvince", null);
Future<Object> confirmationsDTOSFuture = statementThreadUtils.runOneIOLatchCountDown(cmsReportMapper, latch, "queryConfirmationsByMonth", oneStringClass, currentYear);
Future<Object> confirmationsDTOSTotalFuture =statementThreadUtils.runOneIOLatchCountDown(cmsReportMapper,latch,"queryConfirmationsTotal",oneStringClass,currentYear);
latch.await();
List<String> provinces = (List<String>)provincesFuture.get();
provinces.add("其他");
List<ConfirmationsDTO> confirmationsDTOS = (List<ConfirmationsDTO>)confirmationsDTOSFuture.get();
List<ConfirmationsDTO> confirmationsDTOSTotal =(List<ConfirmationsDTO>)confirmationsDTOSTotalFuture.get();
provinces.forEach(province -> {}
result.addFirst(confirmation);
return result;
}catch (Exception e){
e.printStackTrace();
log.error("cms确认书件数统计表SystemError-->"+e.getMessage());
return result;
}
}
具体工具类是如何封装的呢?就是简单的异步代码,用反射调用了mapper接口
当然,还提供了批量处理的工具方法,使用效果如下
String minSignatureDate = cmsReportMapper.queryMinSignatureDate();
String currentDate = DateUtils.currentDate();
if(StringUtils.isEmpty(endTime)){
endTime = currentDate;
}
if(StringUtils.isEmpty(startTime)){
startTime = minSignatureDate;
}
final CountDownLatch latch = new CountDownLatch(9);
StatementThreadUtils statementThreadUtils = new StatementThreadUtils(threadPoolExecutor);
Class[] threeString = {String.class, String.class, String.class};
Class[] twoString = {String.class, String.class};
Object[] threeArgs = {null, startTime, endTime};
Object[] twoArgs = {startTime, endTime};
List<PremiumProvinceDTO> areaSpecialPremium2021 = new ArrayList<>();
List<PremiumProvinceDTO> areaDelayPremium2021 = new ArrayList<>();
List<ConfirmationPremiumDTO> specialPlanPremiumYearByTime = new ArrayList<>();
List<ConfirmationPremiumDTO> delayPremiumYearByTime = new ArrayList<>();
Object[] mappers = {
cmsReportMapper, cmsReportPremiumMapper, cmsReportPremiumMapper,
cmsReportPremiumMapper, cmsReportPremiumMapper, cmsReportPremiumMapper,
cmsReportPremiumMapper, cmsReportPremiumMapper, cmsReportPremiumMapper};
String[] mapperMethodNames = {
"querySaleProvince", "queryAreaNormalPremiumTotal", "queryAreaSpecialPremium",
"queryNormalPremiumYearByTime", "queryAreaSpecialPremium2021", "querySpecialPremiumYearByTime",
"querySpecialPlanPremiumYearByTime", "queryAreaDelayPremium2021", "queryDelayPremiumYearByTime"};
Class[][] methodArgsArr = {
null, threeString, threeString,
twoString, twoString, twoString,
twoString, twoString, twoString};
Object[][] argsArr = {
null, threeArgs, threeArgs,
twoArgs, twoArgs, twoArgs,
twoArgs, twoArgs, twoArgs};
String[] keys = {
"provincesFuture", "areaNormalPremiumTotalFuture", "areaSpecialPremiumFuture",
"normalPremiumYearByTimeFuture", "areaSpecialPremium2021Future", "specialPremiumYearByTimeFuture",
"specialPlanPremiumYearByTimeFuture", "areaDelayPremium2021Future", "delayPremiumYearByTimeFuture"};
HashMap<String, Future> stringFutureHashMap =
statementThreadUtils.runOneIOLatchCountDownMap(keys, mappers, latch, mapperMethodNames, methodArgsArr, argsArr);
latch.await();
List<String> provinces = (List<String>)stringFutureHashMap.get("provincesFuture").get();
provinces.add("其他");
List<PremiumProvinceDTO> areaNormalPremiumTotal = (List<PremiumProvinceDTO>)stringFutureHashMap.get("areaNormalPremiumTotalFuture").get();
List<PremiumProvinceDTO> areaSpecialPremium = (List<PremiumProvinceDTO>)stringFutureHashMap.get("areaSpecialPremiumFuture").get();
List<ConfirmationPremiumDTO> normalPremiumYearByTime = (List<ConfirmationPremiumDTO>)stringFutureHashMap.get("normalPremiumYearByTimeFuture").get();
List<ConfirmationPremiumDTO> specialPremiumYearByTime = (List<ConfirmationPremiumDTO>)stringFutureHashMap.get("specialPremiumYearByTimeFuture").get();
areaSpecialPremium2021.addAll((List<PremiumProvinceDTO>)stringFutureHashMap.get("areaSpecialPremium2021Future").get());
areaDelayPremium2021.addAll((List<PremiumProvinceDTO>)stringFutureHashMap.get("areaDelayPremium2021Future").get());
specialPlanPremiumYearByTime.addAll((List<ConfirmationPremiumDTO> )stringFutureHashMap.get("specialPlanPremiumYearByTimeFuture").get());
delayPremiumYearByTime.addAll((List<ConfirmationPremiumDTO>)stringFutureHashMap.get("delayPremiumYearByTimeFuture").get());
工具类的源码如下
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.*;
public class StatementThreadUtils {
ThreadPoolExecutor threadPoolExecutor = null;
public StatementThreadUtils(ThreadPoolExecutor threadPoolExecutor) {
this.threadPoolExecutor = threadPoolExecutor;
}
public Future<Object> runOneIOLatchCountDown(Object o,CountDownLatch latch, String method,Class[] methodArgs,Object... args){
Future<Object> submit = threadPoolExecutor.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
Object result = null;
try{
Method method1 = o.getClass().getMethod(method,methodArgs);
result = method1.invoke(o, args);
}catch (Exception e){
e.printStackTrace();
}finally {
latch.countDown();
return result;
}
}
});
return submit;
}
public Future<Object> runManyIOLatchCountDown(Object[] objs,CountDownLatch latch, String[] methods,Class[][] methodArgsArr,Object[][] argsArr){
Future<Object> submit = threadPoolExecutor.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
ArrayList<Object> results = new ArrayList<>();
try{
for (int i = 0; i < objs.length; i++) {
Object result = null;
Method method1 = objs[i].getClass().getMethod(methods[i],methodArgsArr[i]);
result = method1.invoke(objs[i], argsArr[i]);
results.add(result);
}
}catch (Exception e){
e.printStackTrace();
}finally {
latch.countDown();
return results;
}
}
});
return submit;
}
public ArrayList<Future> runOneIOLatchCountDownList(Object[] objs,CountDownLatch latch, String[] methods,Class[][] methodArgsArr,Object[][] argsArr){
ArrayList<Future> futures = new ArrayList<>();
for (int i = 0; i < objs.length; i++) {
int finalI = i;
Future<Object> submit = threadPoolExecutor.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
Object result = null;
try{
Method method1 = objs[finalI].getClass().getMethod(methods[finalI],methodArgsArr[finalI]);
result = method1.invoke(objs[finalI], argsArr[finalI]);
}catch (Exception e){
e.printStackTrace();
}finally {
latch.countDown();
return result;
}
}
});
futures.add(submit);
}
return futures;
}
public HashMap<String, Future> runOneIOLatchCountDownMap(Object[] objs,CountDownLatch latch, String[] methods,Class[][] methodArgsArr,Object[][] argsArr){
return runOneIOLatchCountDownMap(methods,objs,latch,methods,methodArgsArr,argsArr);
}
public HashMap<String, Future> runOneIOLatchCountDownMap(String[] keys,Object[] objs,CountDownLatch latch, String[] methods,Class[][] methodArgsArr,Object[][] argsArr){
HashMap<String, Future> futuresMap = new HashMap<>();
for (int i = 0; i < objs.length; i++) {
int finalI = i;
Future<Object> submit = threadPoolExecutor.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
Object result = null;
try{
Method method1 = objs[finalI].getClass().getMethod(methods[finalI],methodArgsArr[finalI]);
result = method1.invoke(objs[finalI], argsArr[finalI]);
}catch (Exception e){
e.printStackTrace();
}finally {
latch.countDown();
return result;
}
}
});
futuresMap.put(keys[finalI],submit);
}
return futuresMap;
}
public static <T> T realTarget(Object target) {
if (Proxy.isProxyClass(target.getClass())) {
MetaObject metaObject = SystemMetaObject.forObject(target);
return realTarget(metaObject.getValue("h"));
}
return (T) target;
}
}
|