数据库连接池的使用

数据库连接池的概念

假如没有连接池,我们操作数据库的流程如下:

  1. 应用程序使用数据库驱动建立和数据库的 TCP 连接 ;

  2. 用户进行身份验证 ;

  3. 身份验证通过,应用进行读写数据库操作 ;

  4. 操作结束后,关闭 TCP 连接 。

 

创建数据库连接是一个比较昂贵的操作,若同时有几百人甚至几千人在线,频繁地进行连接操作将占用更多的系统资源,但数据库支持的连接数是有限的,创建大量的连接可能会导致数据库僵死。

当我们有了连接池,应用程序启动时就预先建立多个数据库连接对象,然后将连接对象保存到连接池中。当客户请求到来时,从池中取出一个连接对象为客户服务。当请求完成时,客户程序调用关闭方法,将连接对象放回池中。

数据库连接池是一种资源管理技术,用于管理和复用数据库连接,避免频繁创建和销毁连接。使用数据库连接池可以提高应用程序的性能和响应速度,节省资源消耗,并提高系统的稳定性。通过池化技术,数据库连接池可以预先创建并维护一定数量的数据库连接,当应用程序需要时可以直接从池中获取连接,使用完毕后归还到池中,从而减少连接建立和销毁的时间和资源消耗。

  1. Apache Commons DBCP‌:这是一个开源的Java数据库连接池,由Apache软件基金会提供。它提供了可配置的、高性能的数据库连接池管理功能。
  2. HikariCP‌:HikariCP是一个轻量级、高性能的数据库连接池,被广泛认为是目前Java平台上性能最好的连接池之一。
  3. C3P0‌:C3P0是另一个流行的Java数据库连接池实现,提供了连接池的标准功能,并支持高级配置选项。
  4. Tomcat JDBC Pool‌:Tomcat JDBC Pool是Apache Tomcat项目的一个子项目,提供了快速、可靠的JDBC连接池实现,与Tomcat服务器集成良好。
  5. Druid‌:Druid是阿里巴巴开源的高性能数据库连接池,提供了强大的监控和扩展功能,被广泛应用于企业级项目中。

 

连接池的优点

1、资源重用:

因为数据库连接可以重用,避免了频繁创建,释放连接引起的大量性能开销,同时也增加了系统运行环境的平稳性。

2、提高性能

当业务请求时,因为数据库连接在初始化时已经被创建,可以立即使用,而不需要等待连接的建立,减少了响应时间。

3、优化资源分配

对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数据库连接数的限制,避免某一应用独占所有的数据库资源。

4、连接管理

数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露。

// druid 数据源
DruidDataSource druidDataSource = new DruidDataSource();
// 数据源配置
druidDataSource.setUrl(JDBC_URL);
druidDataSource.setUsername(USERNAME);
druidDataSource.setPassword(PASSWORD);
// 初始化
druidDataSource.init();
// 获取表名
Connection con = druidDataSource.getConnection();
Statement statement = con.createStatement();
ResultSet resultSet = statement.executeQuery("show tables");
while (resultSet.next()) {
    System.out.println(resultSet.getString(1));
}
con.close();

 

Druid连接池的使用

Druid首先是一个数据库连接池。Druid是目前最好的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。Druid是阿里巴巴开发的号称为监控而生的数据库连接池!

Druid的简单使用

1、 首先在配置文件中导入好Druid连接池的依赖,还有MySQL的依赖

复制代码

 1      <dependency>
 2             <groupId>mysql</groupId>
 3             <artifactId>mysql-connector-java</artifactId>
 4             <version>8.0.33</version>
 5         </dependency>
 6 
 7         <dependency>
 8             <groupId>com.alibaba</groupId>
 9             <artifactId>druid</artifactId>
10             <version>1.2.20</version>
11         </dependency>

复制代码

2、编写Druid的配置文件 druid.properties
1 driverClassName=com.mysql.cj.jdbc.Driver
2 username=root
3 password=root
4 url=jdbc:mysql://localhost:3306/databasename
5 initialSize=5
6 maxActive=10
3、编写测试类进行CRUD操作

 
 1 public class DruidFactory {
 2     @Test
 3     public void testDruidFactory() throws Exception {
 4         Properties properties = new Properties();
 5         InputStream resourceAsStream = DruidFactory.class.getClassLoader().getResourceAsStream("druid.properties");
 6         properties.load(resourceAsStream);
 7 
 8         DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
 9 
10         Connection connection = dataSource.getConnection();
11 
12         String sql = "SELECT * FROM users;";
13 
14         PreparedStatement preparedStatement = connection.prepareStatement(sql);
15 
16         ResultSet resultSet = preparedStatement.executeQuery();
17 
18         ResultSetMetaData metaData = resultSet.getMetaData();
19 
20         int columnCount = metaData.getColumnCount();
21 
22         List<Map> list = new ArrayList<>();
23 
24         while (resultSet.next()){
25             Map map = new HashMap();
26             for (int i = 1; i <= columnCount; i++) {
27                 map.put(metaData.getColumnLabel(i),resultSet.getObject(i));
28             }
29             list.add(map);
30         }
31 
32         Iterator<Map> iterator = list.iterator();
33 
34         while (iterator.hasNext()){
35             System.out.println(iterator.next());
36         }
37 
38         resultSet.close();
39 
40         preparedStatement.close();
41 
42         connection.close();
43 
44     }
45 }

以上就是最基本的Druid连接池的使用,但是在实际上,我们不会每次都进行加载配置文件再获取连接这种操作,所以我们可以封装一个工具类来帮助我们简化连接操作

DruidUtils的简单封装

 1 public class DruidConnection {
 2     private static DataSource dataSource = null;
 3 
 4     static {
 5         Properties properties = new Properties();
 6         InputStream resourceAsStream = DruidConnection.class.getClassLoader().getResourceAsStream("druid.properties");
 7         try {
 8             properties.load(resourceAsStream);
 9             dataSource = DruidDataSourceFactory.createDataSource(properties);
10         }catch (Exception e) {
11             throw new RuntimeException(e);
12         }
13 
14     }
15 
16     public static Connection getConnection() throws SQLException {
17         return dataSource.getConnection();
18     }
19 
20     public static void closeConnect(Connection connection) throws SQLException {
21         connection.close();
22     }
23 }

用静态代码块来获取连接,调用静态方法可以直接获取到连接,这样就简化了每次都要进行加载配置文件等繁琐操作,直接调用静态方法即可获取连接。

Druid对于线程的封装

1 public class DruidThread {
 2     private static DataSource dataSource = null;
 3     private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
 4 
 5     static {
 6         Properties properties = new Properties();
 7         InputStream resourceAsStream = DruidThread.class.getClassLoader().getResourceAsStream("druid.properties");
 8         try {
 9             properties.load(resourceAsStream);
10             dataSource = DruidDataSourceFactory.createDataSource(properties);
11         }  catch (Exception e) {
12             throw new RuntimeException(e);
13         }
14 
15     }
16 
17     public static Connection getConnection() throws SQLException {
18         Connection connection = threadLocal.get();
19         if (connection == null){
20             dataSource.getConnection();
21             threadLocal.set(connection);
22         }
23         return connection;
24     }
25 
26     public static void closeConnection() throws SQLException {
27         Connection connection = threadLocal.get();
28         if (connection != null) {
29             threadLocal.remove();
30             connection.setAutoCommit(true);
31             connection.close();
32         }
33     }
34 }

将connection存放在线程本地量中,同一线程下的各业务逻辑层不需要用参数进行Connection传递,而且可以直接在线程本地量中获取当前i线程的连接,如果没有,那么创建一个新的连接。

 Druid对于Dao层的操作封装
 1 public class BaseDao {
 2     //  DML
 3     public static int executeUpdate(String sql,Object... params) throws SQLException {
 4         Connection connection = DruidConnection.getConnection();
 5         PreparedStatement preparedStatement = connection.prepareStatement(sql,);
 6         for (int i = 1; i <= params.length; i++) {
 7             preparedStatement.setObject(i,params[i-1]);
 8         }
 9         int i = preparedStatement.executeUpdate();
10 
11         preparedStatement.close();
12         DruidConnection.closeConnect(connection);
13         return i;
14     }
15 
16     //  DQL
17     public static <T> List<T> executeQuery(Class<T> clazz,String sql,Object... params) throws SQLException, NoSuchFieldException, InstantiationException, IllegalAccessException {
18 
19         Connection connection = DruidConnection.getConnection();
20 
21         PreparedStatement preparedStatement = connection.prepareStatement(sql);
22 
23         if (params != null && params.length != 0){
24             for (int i = 1; i <= params.length; i++) {
25                 preparedStatement.setObject(i,params[i-1]);
26             }
27         }
28 
29         ResultSet resultSet = preparedStatement.executeQuery();
30         ResultSetMetaData metaData = resultSet.getMetaData();
31 
32         List<T> list = new ArrayList();
33 
34         int columnCount = metaData.getColumnCount();
35 
36         while (resultSet.next()){
37             T t = clazz.newInstance();
38             for (int i = 1; i <= columnCount; i++) {
39                 String columnLabel = metaData.getColumnLabel(i);
40                 Object object = resultSet.getObject(i);
41                 Field declaredField = clazz.getDeclaredField(columnLabel);
42                 declaredField.setAccessible(true);
43                 declaredField.set(t,object);
44             }
45             list.add(t);
46 
47         }
48         resultSet.close();
49 
50         preparedStatement.close();
51 
52         DruidConnection.closeConnect(connection);
53 
54         return list;
55     }
56 }

在Dao层操作数据库进行CRUD操作的时候,每次都要获取连接、创建prepareStatement对象,还要对结果集进行处理,太过于繁琐,于是将CRUD分成两大类进行封装,分别是DML和DQL。

在DQL中,使用集合存取结果集对象,集合的泛型是结果集对象的实体类,通过反射创建该实体类对象,然后通过getDeclaredField获取私有方法,并通过setAccessile设置private属性可访问,最后复制完将对象存到集合中。

 

Spring Boot 方式(推荐)

依赖

    <!--引入druid数据源-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.6</version>
    </dependency>

连接池配置:

spring:
  datasource:
   #数据源基本配置
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/ssm_crud
    type: com.alibaba.druid.pool.DruidDataSource
   #数据源其他配置
    druid:
      #配置初始化大小、最小、最大线程数
      initialSize: 5
      minIdle: 5
      #CPU核数+1,也可以大些但不要超过20,数据库加锁时连接过多性能下降
      maxActive: 20
     # 最大等待时间,内网:800,外网:1200(三次握手1s)
      maxWait: 60000
      timeBetweenEvictionRunsMillis: 60000
     #配置一个连接在池中最大空间时间,单位是毫秒
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1 
      testWhileIdle: true
     # 设置从连接池获取连接时是否检查连接有效性,true检查,false不检查
      testOnBorrow: true
      # 设置从连接池归还连接时是否检查连接有效性,true检查,false不检查
      testOnReturn: true
      #可以支持PSCache(提升写入、查询效率)
      poolPreparedStatements: true
      #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙  
      filters: stat,wall,log4j
      maxPoolPreparedStatementPerConnectionSize: 20
      useGlobalDataSourceStat: true  
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

Druid连接池的主要配置项

 

配置 Druid 监控

Druid 提供了 Web 监控页面,可以查看连接池的状态和 SQL 监控等信息。要启用监控,需要添加以下配置:

Druid 提供了 Web 监控页面,可以查看连接池的状态和 SQL 监控等信息。要启用监控,需要添加以下配置:

# 配置 Druid 监控
spring:
  datasource:
    druid:
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
      web-stat-filter:
        enabled: true
        url-pattern: /*
      # 监控用户名和密码,用于登录监控页面
      username: admin
      password: admin


 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛定谔的猫1982

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值