背景
最近遇到多数据库执行sql处理后生成报表,项目原有逻辑是单线程同步执行的,随着数据量的增大时间越来越长,我忍不住要优化。考虑一定得用多线程处理,那就首先需要线程池了,毕竟没有线程池的情况下很容易出现无休止创建线程是非常危险的。
实现
代码
伪代码
//创建线程池
ExecutorService executor = Executors.newFixedThreadPool(4);
CompletionService<Object> completionSevice = new ExecutorCompletionService<Object>(executor);
for (DatabaseNode databaseNode: databaseNodeList) {
//提交请求到具体的库
completionSevice.submit(new SqlHandler(properties, databaseNode, sql,
ModuleConstant.NODE_PASSWORD_KEY, encode, userType));
}
//获取执行结果
//databaseNodeList.size()只为获取节点个数,获取到的每个节点是没有顺序
for (int i = 0; i < databaseNodeList.size(); i++) {
Object result = null;
DrdsNodes nodeSchema = null;
//获取到每个库节点执行的结果,结果是从最先执行完的开始拿的,每次拿一个
Map<DrdsNodes, Object> resultMap = (Map<DatabaseNode , Object>)
completionSevice.take().get();
//后续根据具体业务操作
。。。。
}
/**
*具体执行sql的类 (伪代码)
*
*/
public class SqlHandler implements Callable<Object> {
private final String ddl;
private JdbcTemplate jdbcTemplate;
private DrdsNodes databaseNode;
public SqlRowNumHandler(RdsProperties properties, DatabaseNode databaseNode, String ddl, String nodePwdKey,String encode, Integer userType) {
//sql的构建封装代码
}
//类实现Callable<Object>,初始化时自动调用此方法
@Override
public Object call() throws Exception {
//key 是数据库的实例,用以获取时区分是哪个库执行的结果
Map<DrdsNodes, Object> resultMap = Maps.newHashMap();
try {
Object value = jdbcTemplate.update(ddl);
resultMap.put(drdsNode, value);
return resultMap;
} catch (Exception e) {
resultMap.put(drdsNode, e.getMessage());
return resultMap;
}
}
注:completionSevice.take().get();是类似于指针一样获取结果(先执行完返回的先获取到)
CompletionService实现原理
CompletionService实际上可以看做是Executor和BlockingQueue的结合体。CompletionService在接收到要执行的任务时,通过类似BlockingQueue的put和take获得任务执行的结果。CompletionService的一个实现是ExecutorCompletionService,ExecutorCompletionService把具体的计算任务交给Executor完成。
测试后发现效率提高了5倍左右。