小能豆

springboot里hikari连接mysql耗尽连接的疑问

javascript

1、用springboot里的org.quartz.Scheduler组件时,一开始使用不当,创建了一千来个schedule,里边有一段逻辑,是会连接一些mysql表进行查询的逻辑。
前些天每隔一段时间就会报连接池拿不到连接了,推测是因为连接被耗尽。
2、后来改了一下代码,只创建一个schedule,但是在里边循环一千来次这个逻辑,去读mysql表。
3、如果再进行改造,对这一千次循环拆分成多线程来跑呢?
我的疑问是,这3种情况,到底对mysql连接池产生的并发访问压力是什么样的?

我是在springboot里用的hikari组件对mybatis进行的管理,所以连接的释放啊、锁啊之类的都没关注,都是让框架自己管理了,然后多线程也是用的hutool里的threadutil.execute();也没有去定多少个线程数什么的。


阅读 37

收藏
2024-06-19

共1个答案

小能豆

在使用Spring Boot和Quartz调度任务时,你遇到的问题主要与MySQL连接池的管理和并发访问有关。我们可以通过分析这三种情况,来了解它们对MySQL连接池产生的并发访问压力以及可能的改进方法。

情况1:创建了1000个Scheduler

在这种情况下,1000个Scheduler独立运行,它们可能同时访问MySQL数据库。如果每个Scheduler都在同一时间进行数据库查询,这会导致非常高的并发数据库连接请求,可能导致连接池耗尽。

并发访问压力
- 每个Scheduler都可能在同一时间进行数据库操作。
- 如果连接池大小不够,很多Scheduler会因为无法获取连接而等待,导致连接池耗尽。
- 这种情况对数据库服务器的压力非常大,可能导致性能问题。

情况2:创建一个Scheduler,循环1000次

在这种情况下,只有一个Scheduler在运行,但它循环1000次进行数据库操作。虽然Scheduler的数量减少了,但循环中的每次操作还是会进行数据库访问。

并发访问压力
- 由于是单线程循环操作,数据库连接的并发请求数会比情况1少。
- 连接池的压力会减小,但由于所有操作都在单线程中进行,总体运行时间可能会变长。
- 这种方式不会立即耗尽连接池,但性能依然可能受到影响,因为一次性操作的次数太多。

情况3:使用多线程进行1000次循环操作

将1000次循环操作拆分成多线程执行,这样可以利用多线程的并发处理能力,加快任务执行速度。但是,这样也会增加并发数据库访问请求。

并发访问压力
- 并发线程数将直接影响到数据库的并发请求数。
- 如果线程数过多,可能会导致连接池耗尽,类似于情况1。
- 需要合理设置线程数和连接池大小,来平衡性能和资源利用。

建议改进方法

  1. 配置连接池
    确保你的Hikari连接池配置合理,特别是maximumPoolSize(最大连接数)。根据实际需求和数据库服务器的处理能力来调整连接池的大小。

yaml spring.datasource.hikari.maximum-pool-size: 50

  1. 控制并发线程数
    在多线程执行时,不要无限制地增加线程数。可以使用ThreadPoolExecutor来管理线程池,并设置合理的线程数上限。

```java
// 创建固定大小的线程池
int corePoolSize = 10; // 根据实际情况设置
ExecutorService executorService = Executors.newFixedThreadPool(corePoolSize);

for (int i = 0; i < 1000; i++) {
executorService.execute(() -> {
// 你的逻辑
});
}

executorService.shutdown();
```

  1. 批量处理
    尽量使用批量查询来减少数据库连接的次数。例如,如果每次查询的数据量不是很大,可以一次性查询多个记录,然后在内存中进行处理。

  2. 连接池监控
    监控连接池的使用情况,调整连接池参数和并发策略,确保连接池不会被耗尽。

  3. 异步调度
    使用异步调度来分散任务执行时间,避免瞬时高并发。例如,可以在调度任务中引入一定的延迟或调度策略,使任务执行更加均匀。

示例:使用Hutool的ThreadUtil和ThreadPoolExecutor

import cn.hutool.core.thread.ThreadUtil;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SchedulerTask {

    public static void main(String[] args) {
        int corePoolSize = 10; // 设置合理的线程池大小
        ExecutorService executorService = Executors.newFixedThreadPool(corePoolSize);

        for (int i = 0; i < 1000; i++) {
            executorService.execute(() -> {
                // 你的逻辑,例如数据库查询
                queryDatabase();
            });
        }

        executorService.shutdown();
    }

    private static void queryDatabase() {
        // 数据库查询逻辑
    }
}

通过以上方法,可以更好地控制并发访问对MySQL连接池的压力,确保应用程序的稳定性和性能。

2024-06-19