druid作为数据库连接池的几种配置总结,通过debug去跟配置结果

本文详细介绍SpringBoot中数据库连接池的配置方法,包括默认的HikariCP、纯YML配置的Druid连接池、借助配置文件和Java类注入属性的配置方式,以及常见问题的解决策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

检测方法--后面的测试结果,debug结果都是基于该测试类测试。

一、数据库默认的数据连接池

二、纯yml配置druid阿里巴巴数据库连接池(强烈推荐方式,我也是这么配置的)

三、需要借助配置文件,Java类来注入属性的配置

四、注入同三,只是注入配置文件可以改成自己对应属性注入,有yml基础的看的懂,方式差不多

踩坑点总结


整篇文章基于SpringBoot2.2.6编写。不同SpringBoot配置存在某些差异,后面会提。

检测方法--后面的测试结果,debug结果都是基于该测试类测试。

作用:用于检测数据库连接池是否生效,属性是否注入等。

测试类如下,通过打断点测试

@SpringBootTest
public class DataSourceTest {

    @Autowired
    DataSource dataSource;

    @Test
    void contexts() throws SQLException {
        //测试数据源、通过数据源拿到的连接种类
        System.out.println(dataSource.getClass());
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        connection.close();
    }
}

此方法每次可以根据输出结果观察到数据源是哪种,连接是哪种

测试属性是否注入,断点位置如下

通过debug启动,然后查看dataSource的属性,对于我,一般只要查看initialSize=0就是未注入成功

 

下面进入正题

一、数据库默认的数据连接池

application.yml配置

spring:
#数据源配置,使用默认的数据库连接池
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/essm_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
    username: essm_dev
    password: essm123456

注意:如果遇到什么错,基本上是url:jdbc那个属性后面的配置没有写正确,时区啥的

进行测试,通过test打印数据源类,数据连接类型如下图

SpringBoot2.2.6默认是使用的是HikariDataSource作为数据库连接池

 

二、纯yml配置druid阿里巴巴数据库连接池(强烈推荐方式,我也是这么配置的)

依赖:使用strarer启动器作为依赖,便可以实现纯yml配置

        <!--数据源配置,纯yml配置,结合springboot的druid_start启动器实现-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

yml配置信息如下:

spring:
#数据源配置,纯yml配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/essm_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
    username: essm_dev
    password: essm123456
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      # 初始化大小,最小,最大活跃数
      initial-size: 8
      min-idle: 8
      max-active: 20
      # 配置获取连接等待超时的时间,单位是毫秒
      max-wait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      time-between-eviction-runs-millis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      min-evictable-idle-time-millis: 30000
      max-evictable-idle-time-millis: 300000
      validation-query: SELECT 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      # 打开PSCache,并且指定每个连接上PSCache的大小
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙,slf4j(用log4j需要额外导入依赖,版本不同,这里也可以去掉slf4j)
      filters: stat,wall,slf4j
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
      connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      # 配置监控服务器
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        reset-enable: true
        login-username: druid
        login-password: druid123

debug测试结果:

1、属性都正常注入,成功

2、输入结果拿到的是阿里巴巴druid数据库自动配置starater的连接池和连接

3、启动应用程序后--不是测试啦,登录localhost:8080/druid出现数据库监测界面

4、输入yml配置文件的账户密码,druid,druid123,登录成功,结束

 

三、需要借助配置文件,Java类来注入属性的配置

导入的是普通的阿里巴巴druid依赖,这里不支持yml纯配置,不是starter,检测就是debug你会发现数据注入不成功。

依赖:

<!-- 加入阿里巴巴连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>

还是用原来的yml配置文件,未修改

debug测试结果

1、属性对不上,注入失败

2、数据库连接池是druid,拿到的连接是普通的数据库连接

难道是yml配置问题?修改一下,将yml配置的druid节点去掉再次测试

新的配置文件

spring:
  #禁用thymeleaf
  thymeleaf:
    cache: false

#数据源配置,配置文件DruidConfig配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/essm_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
    username: essm_dev
    password: essm123456
    type: com.alibaba.druid.pool.DruidDataSource
    #druid:
    # 初始化大小,最小,最大活跃数
    initial-size: 8
    min-idle: 8
    max-active: 20
    # 配置获取连接等待超时的时间,单位是毫秒
    max-wait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    time-between-eviction-runs-millis: 60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    min-evictable-idle-time-millis: 30000
    max-evictable-idle-time-millis: 300000
    validation-query: SELECT 1
    test-while-idle: true
    test-on-borrow: false
    test-on-return: false
    # 打开PSCache,并且指定每个连接上PSCache的大小
    pool-prepared-statements: true
    max-pool-prepared-statement-per-connection-size: 20
    #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat,wall,slf4j
    # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
    connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
    # 配置监控服务器
    stat-view-servlet:
      enabled: true
      url-pattern: /druid/*
      reset-enable: true
      login-username: druid
      login-password: druid123

测试结果还是一样,这里不贴结果

其实是要自己编写属性配置文件,进行注入

yml配置文件

spring:
  #禁用thymeleaf
  thymeleaf:
    cache: false

#数据源配置,配置文件DruidConfig配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/essm_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
    username: essm_dev
    password: essm123456
    type: com.alibaba.druid.pool.DruidDataSource
    #druid:
    # 初始化大小,最小,最大活跃数
    initial-size: 8
    min-idle: 8
    max-active: 20
    # 配置获取连接等待超时的时间,单位是毫秒
    max-wait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    time-between-eviction-runs-millis: 60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    min-evictable-idle-time-millis: 30000
    max-evictable-idle-time-millis: 300000
    validation-query: SELECT 1
    test-while-idle: true
    test-on-borrow: false
    test-on-return: false
    # 打开PSCache,并且指定每个连接上PSCache的大小
    pool-prepared-statements: true
    max-pool-prepared-statement-per-connection-size: 20
    #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat,wall,slf4j
    # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
    connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
    # 配置监控服务器
    stat-view-servlet:
      enabled: true
      url-pattern: /druid/*
      reset-enable: true
      login-username: druid
      login-password: druid123

属性注入类DruidConfig


package com.essm.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.HashMap;


/**
 *
 * druid数据源具有监控的功能,并提供了一个 web 界面方便用户查看
 *
 * @author :HUANG ZHI XUE
 * @date :Create in 2020-08-04
 */


@Configuration
public class DruidConfig {
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

    //后台监控:因为springboot内置了servlet容器,使用没有web.xml,可以用ServletRegistrationBean替代
    @Bean
    public ServletRegistrationBean statViewServlet() {
        ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");

        //后台需要有人登陆,账号密码
        HashMap<String, String> initParameters = new HashMap<>();

        //增加配置
        initParameters.put("loginUsername", "admin");  //登陆参数是固定的
        initParameters.put("loginPassword", "123456");

        //允许谁可以访问
        initParameters.put("allow", "");


        bean.setInitParameters(initParameters);  //设置初始化参数
        return bean;
    }

}

重新测试,测试结果

1、属性注入成功

2、输出结果,拿到的是普通的druid数据源和连接

3、启动主程序,登录监控localhost:8080/druid,输入账户密码admin/admin123成功

 

四、注入同三,只是注入配置文件可以改成自己对应属性注入,有yml基础的看的懂,方式差不多

配置文件DruidConfig给出如下

package com.essm.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 *
 * 数据源具有监控的功能,并提供了一个 web 界面方便用户查看
 * Druid数据库连接池配置文件
 *
 * @author :HUANG ZHI XUE
 * @date :Create in 2020-08-04
 */

//@Configuration
public class DruidConfig {
    private static final Logger logger = LoggerFactory.getLogger(DruidConfig.class);
    @Value("${spring.datasource.url}")
    private String dbUrl;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;
    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;
    @Value("${spring.datasource.druid.initial-size}")
    private int initialSize;
    @Value("${spring.datasource.druid.max-active}")
    private int maxActive;
    @Value("${spring.datasource.druid.min-idle}")
    private int minIdle;
    @Value("${spring.datasource.druid.max-wait}")
    private int maxWait;
    @Value("${spring.datasource.druid.pool-prepared-statements}")
    private boolean poolPreparedStatements;
    @Value("${spring.datasource.druid.max-pool-prepared-statement-per-connection-size}")
    private int maxPoolPreparedStatementPerConnectionSize;
    @Value("${spring.datasource.druid.time-between-eviction-runs-millis}")
    private int timeBetweenEvictionRunsMillis;
    @Value("${spring.datasource.druid.min-evictable-idle-time-millis}")
    private int minEvictableIdleTimeMillis;
    @Value("${spring.datasource.druid.max-evictable-idle-time-millis}")
    private int maxEvictableIdleTimeMillis;
    @Value("${spring.datasource.druid.validation-query}")
    private String validationQuery;
    @Value("${spring.datasource.druid.test-while-idle}")
    private boolean testWhileIdle;
    @Value("${spring.datasource.druid.test-on-borrow}")
    private boolean testOnBorrow;
    @Value("${spring.datasource.druid.test-on-return}")
    private boolean testOnReturn;
    @Value("${spring.datasource.druid.filters}")
    private String filters;
    @Value("{spring.datasource.druid.connection-properties}")
    private String connectionProperties;

    /**
     * Druid 连接池配置
     */
    @Bean     //声明其为Bean实例
    public DruidDataSource dataSource() {
        DruidDataSource datasource = new DruidDataSource();
        datasource.setUrl(dbUrl);
        datasource.setUsername(username);
        datasource.setPassword(password);
        datasource.setDriverClassName(driverClassName);
        datasource.setInitialSize(initialSize);
        datasource.setMinIdle(minIdle);
        datasource.setMaxActive(maxActive);
        datasource.setMaxWait(maxWait);
        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        datasource.setMaxEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        datasource.setValidationQuery(validationQuery);
        datasource.setTestWhileIdle(testWhileIdle);
        datasource.setTestOnBorrow(testOnBorrow);
        datasource.setTestOnReturn(testOnReturn);
        datasource.setPoolPreparedStatements(poolPreparedStatements);
        datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
        try {
            datasource.setFilters(filters);
        } catch (Exception e) {
            logger.error("druid configuration initialization filter", e);
        }
        datasource.setConnectionProperties(connectionProperties);
        return datasource;
    }

    /**
     * 配置 Druid 监控界面
     */
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean srb =
                new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
        //设置控制台管理用户
        srb.addInitParameter("loginUsername","root");
        srb.addInitParameter("loginPassword","root");
        //是否可以重置数据
        srb.addInitParameter("resetEnable","false");
        return srb;
    }

/*    @Bean
    public FilterRegistrationBean statFilter(){
        //创建过滤器
        FilterRegistrationBean frb =
                new FilterRegistrationBean(new WebStatFilter());
        //设置过滤器过滤路径
        frb.addUrlPatterns("/*");
        //忽略过滤的形式
        frb.addInitParameter("exclusions",
                "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return frb;
    }*/
}

yml配置

spring:
#数据源配置,纯yml配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/essm_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
    username: essm_dev
    password: essm123456
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      # 初始化大小,最小,最大活跃数
      initial-size: 8
      min-idle: 8
      max-active: 20
      # 配置获取连接等待超时的时间,单位是毫秒
      max-wait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      time-between-eviction-runs-millis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      min-evictable-idle-time-millis: 30000
      max-evictable-idle-time-millis: 300000
      validation-query: SELECT 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      # 打开PSCache,并且指定每个连接上PSCache的大小
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙,slf4j(用log4j需要额外导入依赖,版本不同,这里也可以去掉slf4j)
      filters: stat,wall,slf4j
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
      connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      # 配置监控服务器
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        reset-enable: true
        login-username: druid
        login-password: druid123

 

踩坑点总结

版本问题1:

版本问题会导致很多问题,如比较低的SpringBoot版本,在配置里面,很多加了log4j会不会报错,但是后面版本高的SpringBoot就会报错,需要去掉log4j或者加入log4j依赖或者改成slf4j

 

版本问题2:

看到很多在DruidConfig里面配了过滤器这个在同样在低版本中的SpringBoot可以,但是高版本也不支持,直接去掉就行了。

 

jdbc版本问题:

driver依赖6以上的驱动是配置cj的,url时区问题常发生错误,注意配置后面属性&serverTimezone=GMT%2B8

 

差不多了吧,只是简单总结一下。

 

<think>嗯,用户问的是“如何查询并发太大会导致连接池满吗”。首先,我需要确认用户的问题到底是什么。看起来用户想知道当并发量很大时,数据库连接池是否会被占满,以及如何检测这种情况。可能用户在实际开发中遇到了性能问题,或者他们正在设计一个高并发的系统,想提前了解潜在的问题。 用户之前的问题是关于一次数据库查询是否会增加连接池的连接数,现在这个问题是相关的,可能他们在理解连接池的工作机制之后,进一步想了解高并发的影响。因此,我需要先回顾连接池的基本原理,然后解释高并发如何导致连接池满,再说明如何查询和诊断这种情况。 首先,连接池的作用是复用连接,减少频繁创建和销毁的开销。当并发请求增加时,每个请求都需要从连接池中获取一个连接。如果并发数超过连接池的最大容量(maxTotal或类似参数),新的请求就需要等待或者失败,导致连接池满。这时候,应用可能会抛出异常,比如Cannot get a connection, pool error,或者响应时间变长,甚至超时。 接下来,用户想知道如何查询这种情况。可能需要从几个方面入手:监控连接池的状态,查看日志是否有相关错误,分析系统的性能指标。比如,通过连接池提供的监控接口,获取活跃连接数、空闲连接数、等待线程数等数据。另外,应用日志中可能会有无法获取连接的异常记录,这也是一个线索。 还要考虑不同的连接池实现可能有不同的配置和监控方式,比如HikariCP、Druid、Tomcat JDBC Pool等,各自有不同的配置参数和监控端点。需要给出通用的方法,但也可以提到常见连接池的具体处理方式。 此外,用户可能想知道如何预防连接池被撑满,比如设置合理的maxTotal大小,优化查询性能,使用超时和重试机制,以及及时释放连接。这部分可以作为解决方案的一部分,帮助用户不仅诊断问题,还能预防和解决问题。 需要确保回答结构清晰,分步骤说明如何检测连接池是否因为高并发而满,可能的原因,以及解决方法。同时,使用用户容易理解的语言,避免过于技术化的术语,但又要准确。 可能还需要提醒用户,连接池满不一定只是因为并发高,也可能是因为连接泄漏,即获取连接后没有正确释放,导致连接无法回到池中。这时候即使并发不高,连接池也会被逐渐占满。因此,诊断时需要排除这种情况,比如检查代码中是否有未关闭的连接,或者使用连接池的泄漏检测功能。 总结回答的结构:先解释连接池满的原因,然后分步骤说明如何查询和确认,接着分析可能的原因,最后给出解决方案和预防措施。这样用户能够逐步排查问题,找到根源并解决。</think>### 逐步解析:如何判断并发是否导致连接池满? --- #### **1. 连接池满的直接表现** 当并发请求超过连接池容量时,会出现以下现象: - **错误日志**:出现 `Cannot get a connection from pool`、`Timeout waiting for connection` 等异常。 - **响应变慢**:因等待空闲连接,请求排队时间增加,接口延迟上升。 - **监控指标异常**:活跃连接数(Active Connections)达到 `maxTotal` 上限,且等待线程数(Wait Threads)持续增长。 --- #### **2. 查询连接池状态的 3 种方法** ##### **方法 1:通过监控工具(推荐)** - **连接池内置指标**(如 HikariCP、Druid): - **活跃连接数**:`activeConnections`(已分配的连接数量)。 - **空闲连接数**:`idleConnections`(池中可立即使用的连接)。 - **等待线程数**:`threadsAwaitingConnection`(阻塞等待连接的请求数)。 - **示例**:若 `active + idle = maxTotal` 且 `threadsAwaiting > 0`,说明连接池已满。 - **监控平台集成**(如 Prometheus + Grafana): ```bash # HikariCP 指标示例 hikaricp_connections_active{pool="myPool"} hikaricp_connections_idle{pool="myPool"} hikaricp_pending_threads{pool="myPool"} ``` ##### **方法 2:通过日志分析** - **连接池配置日志**:开启连接池的详细日志(如 HikariCP 的 `DEBUG` 级别日志)。 ```java // HikariCP 配置示例 dataSource.setLogWriter(new PrintWriter(System.out)); // 打印连接池操作日志 ``` - **错误日志筛选**:搜索 `Connection is not available`、`Timeout` 等关键字。 ##### **方法 3:通过代码主动查询** - **调用连接池 API**(以 Druid 为例): ```java DruidDataSource dataSource = (DruidDataSource) druidDataSource; int activeCount = dataSource.getActiveCount(); // 活跃连接数 int poolingCount = dataSource.getPoolingCount(); // 空闲连接数 int waitThreadCount = dataSource.getWaitThreadCount(); // 等待线程数 ``` --- #### **3. 并发导致连接池满的根本原因** | 原因 | 说明 | 解决方案 | |---------------------|----------------------------------------------------------------------|-----------------------------------| | **突发流量** | 瞬时请求量超过 `maxTotal`,如秒杀活动 | 限流(Sentinel、Hystrix) | | **慢 SQL** | 单个查询耗时过长,占用连接时间增加 | SQL 优化、增加索引 | | **连接泄漏** | 未正确释放连接(如未调用 `close()`) | 代码审查 + 连接泄漏检测工具 | | **配置不合理** | `maxTotal` 设置过低,或 `minIdle` 未预热 | 根据压测调整参数 | --- #### **4. 预防连接池满的实践建议** 1. **动态调整配置**: - 根据压测结果设置 `maxTotal`(公式:`maxTotal ≈ 平均查询耗时(ms) × QPS / 1000`)。 - 预热连接池:启动时初始化 `minIdle` 连接,避免首次请求延迟。 2. **优化资源使用**: - 为耗时操作(如事务)**单独分配连接池**,避免阻塞短查询。 - 使用 `SET STATEMENT_TIMEOUT` 限制单条 SQL 执行时间。 3. **兜底策略**: - 设置连接获取超时时间(如 HikariCP 的 `connectionTimeout=30s`)。 - 配置连接池的淘汰机制(如 Druid 的 `removeAbandoned=true`)。 --- ### **总结** 并发是否导致连接池满,可通过以下步骤验证: 1. 监控 `活跃连接数` 是否达到 `maxTotal`。 2. 检查日志中是否有连接超时错误。 3. 分析慢 SQL 或代码泄漏问题。 **最终结论**:若并发请求数持续超过 `maxTotal`,或单个连接占用时间过长,必然导致连接池满。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值