增加索引 + 异步 + 不落地后,从 12h 优化到 15 min
在开发中,我们经常会遇到这样的需求,将数据库中的图片导出到本地,再传给别人。
一、一般我会这样做:
- 通过接口或者定时任务的形式。
- 读取Oracle或者MySQL数据库。
- 通过FileOutputStream将Base64解密后的byte[]存储到本地。
- 遍历本地文件夹,将图片通过FTP上传到第三方服务器。
现场炸锅了!
实际的数据量非常大,据统计差不多有400G的图片需要导出。
现场人员的反馈是,已经跑了12个小时了,还在继续,不知道啥时候能导完。
停下来呢?之前的白导了,不停呢?不知道要等到啥时候才能导完。
这不行啊,速度太慢了,一个简单的任务,不能被这东西耗死吧?
@Value("${months}")
private String months;
@Value("${imgDir}")
private String imgDir;
@Resource
private UserDao userDao;
@Override
public void getUserInfoImg() {
try {
// 获取需要导出的月表
String[] monthArr = months.split(",");
for (int i = 0; i < monthArr.length; i++) {
// 获取月表中的图片
Map map = new HashMap();
String tableName = "USER_INFO_" + monthArr[i];
map.put("tableName", tableName);
map.put("status", 1);
List userInfoList = userDao.getUserInfoImg(map);
if (userInfoList == null || userInfoList.size() == 0) {
return;
}
for (int j = 0; j < userInfoList.size(); j++) {
UserInfo user = userInfoList.get(j);
String userId = user.getUserId();
String userName = user.getUserName();
byte[] content = user.getImgContent;
// 下载图片到本地
FileUtil.dowmloadImage(imgDir + userId+"-"+userName+".png", content);
// 将下载好的图片,通过FTP上传给第三方
FileUtil.uploadByFtp(imgDir);
}
}
} catch (Exception e) {
serviceLogger.error("获取图片异常:", e);
}
}
二、谁写的?赶紧加班优化,会追责吗?
经过1小时的深思熟虑,慢的原因可能有以下几点:
- 查询数据库
- 程序串行
- base64解密
- 图片落地
- FTP上传到服务器
在使用@Async时,如果不指定线程池的名称,也就是不自定义线程池,@Async是有默认线程池的,使用的是Spring默认的线程池SimpleAsyncTaskExecutor。
默认线程池的默认配置如下:
- 默认核心线程数:8。
- 最大线程数:Integet.MAX_VALUE。
- 队列使用LinkedBlockingQueue。
- 容量是:Integet.MAX_VALUE。
- 空闲线程保留时间:60s。
- 线程池拒绝策略:AbortPolicy。
从最大线程数可以看出,在并发情况下,会无限制的创建线程,我勒个吗啊。