记一次线上调优:我用线程池ThreadPoolExecutor处理任务和Redis做缓存查询,将程序效率提升了5倍!

痛苦的源头

我们线上有个小程序,主要用来实时查询某省所有地市上报的一些信息,这些信息在后台,每5分钟推送一次Excel文件到我们的Kafka服务,这些信息就存储在Excel文件中。

我要做的是,监听Kafka消息,得到Excel文件后,处理16个地市的消息,并将结果存储为图片(比较耗时),处理完之后,提供给小程序查询。

为了追求开发速度,我直接按照地市顺序解析了Excel,截图,将图片url以及一些相关信息存储在MySQL数据库。

但是,问题出现了:

  • 虽然勉强能够保证大部分时候5分钟内能处理完16个地市的数据,但是Excel内容多的时候,在5分钟内信息有时处理不完;
  • 抛开5分钟内处理不完数据的情况,还有查询上的速度也很慢,这对于前端小程序用户来说,是不能够忍受的

解决问题

针对出现的问题,我分别对数据处理和查询两部分内容作了优化。

线性处理改为并行处理

之前按照顺序处理16个地市的数据,运行速度实在太慢,于是考虑将地市分开来处理,每个地市开启一个线程,该线程只跑自己的数据。

因此,我准备一个线程池:

static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(8,
            16,
            0,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(128),
            new ThreadPoolExecutor.DiscardPolicy());

只要监听到有数据推送(一次推送16个地市的数据)过来,我就开启16个线程放到线程池中,让他们并行处理:

//开启16个线程,跑16个地市的数据
String[] cityArray = cities.split(",");
List<Future> futures = new ArrayList<>();
for (String city : cityArray) {
    ProcessRunnable runnable = new ProcessRunnable(city, baseData, siteAlarmListener, redisTemplate);
    Future future = threadPool.submit(runnable);
    futures.add(future);
}

for (Future future : futures) {
    try {
        future.get();
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
}

这样一番处理之后,经过多次实测,执行速度从原来的5分钟提升到了1分钟之内处理完毕(解析,填充模板,截图等一系列处理)!速度提升了5倍!

缓存数据&查询:我选Redis

Redis既能做数据库,也能做缓存,我们的小程序具体需求不太看重历史数据,主要目的是要获取当前最新的信息。

基于此种需求,我直接将最新的数据放到Redis,这样查询的时候就快了(直接从内存中查)!然后做一个定时任务,将数据持久化的MySQL。

查询基本上是秒出结果:

在这里插入图片描述

附上部分代码:

  • 引入spring-data-redis
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 配置redis服务器
spring:
  redis:
    host: you ip
    port: you redis port
    password: 有则填写,无则不需要此属性
  • 保存数据

需要将数据准备好,我是把数据放在list中了,然后用StringRedisTemplate进行操作。

//StringRedisTemplate
private StringRedisTemplate redisTemplate;

@Autowired
public void setRedisTemplate(StringRedisTemplate redisTemplate) {
    this.redisTemplate = redisTemplate;
}
LOGGER.info("===========开始保存数据到redis==========");
//此处需要准备好数据,我是将数据放在list中了,然后
redisTemplate.opsForValue().set("sal", JSON.toJSONString(list));
LOGGER.info("===========保存到redis end==========");
  • 查询数据
String result = redisTemplate.opsForValue().get("sal");
List<YourEntity> resultList = JSONObject.parseArray(result, YourEntity.class);

小结

本次线上调优,通过数据处理和数据存储两个方面对代码进行了优化。

把原来的串行处理模式改成使用线程池的方式进行处理,大大缩减了数据处理的时间;

在查询方面,从传统的数据库查询改成查缓存,查询效率也有了质的提升!

  • 线程池,池化技术
  • Redis,快

欢迎阅读我的其他Java基础文章

看完点赞,养成习惯。

举手之劳,赞有余香。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值