增加索引 + 异步 + 不落地后,从 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。

从最大线程数可以看出,在并发情况下,会无限制的创建线程,我勒个吗啊。