Algum resumo do pool de conexões do banco de dados

Continue a criar, acelere o crescimento! Este é o 4º dia da minha participação no "Nuggets Daily New Plan · June Update Challenge", [Clique para ver os detalhes do evento]

O que é pool de conexões

O pool de conexão do banco de dados serve para criar um certo número de objetos de conexão do banco de dados quando o programa é inicializado e salvá-lo em uma área de memória, o que permite que a aplicação reutilize uma conexão de banco de dados existente ao invés de restabelecer uma ; liberar o tempo ocioso que excede o tempo ocioso máximo Conexões de banco de dados cronometradas para evitar conexões de banco de dados ausentes devido à não liberação de conexões de banco de dados.

Por que usar o pool de conexões?

De fato, quando o volume de negócios não é grande e a simultaneidade não é grande, por exemplo, se escrevermos nossos próprios experimentos, a conexão pode ser estabelecida temporariamente. No entanto, na empresa, temos uma grande quantidade de simultaneidade, chegando a níveis de 100 ou 1000. As operações de estabelecimento e fechamento de conexões causarão gargalos de desempenho, portanto, os pools de conexões devem ser considerados para otimizar as operações:

  1. Retire a conexão (quando o serviço de negócios for iniciado, inicialize várias conexões e coloque-as no armazenamento de conexão)
  2. Envie uma solicitação (quando houver uma solicitação, busque-a no armazenamento de conexão)
  3. Coloque de volta a conexão (após a execução, a conexão é colocada de volta no armazenamento de conexão)

Aqui, a estrutura de dados do armazenamento de conexão e manutenção da conexão é o pool de conexões.

A ideia de tecnologia de pool é principalmente reduzir o consumo de recursos adquiridos a cada vez e melhorar a utilização dos recursos.

Sobre commons-dbutils

Depois de aprender a arquitetura de três camadas , sabemos que para o método geral  de adição, exclusãoexecute(String sql ,Object[] os) e modificação  , desde que o parâmetro sql e o parâmetro de substituição os sejam passados, a função de adição, exclusão e modificação correspondente pode ser realizada; mas para o método de consulta  query(String sql, Object[] os) , para poder ser universal , só pode ser encapsulado no conjunto de resultados  ResultSet , mas não pode continuar a ser encapsulado em tipos como objetos ou coleções.

Para resolver o problema de [encapsulamento completo de adições, exclusões, alterações ou verificação ], temos que usar uma  commons-dbutils biblioteca de classes para implementá-lo.

commons-dbutils 是 Apache 组织提供的一个 JDBC 工具类库,极大地简化了 JDBC 的代码量,并且不会影响程序的性能。

commons-dbutils 类库主要包含了两个类和一个接口,如下表:

全 名 类 或 接 口
org.apache.commons.dbutils.DbUtils class
org.apache.commons.dbutils.QueryRunner class
org.apache.commons.dbutils.ResultSetHandler intereface

DbUtils 类

DbUtils 是一个工具类,提供了关闭连接、事务提交/回滚、注册 JDBC 驱动程序等常用方法。DbUtils 类中的方法都是 public static 修饰的(除了构造方法),常用方法如下图所示。

图片描述

QueryRunner 类

QueryRunner 类主要用于执行增删改查等 SQL 语句。特别的,如果执行的是查询 SQL,还需要结合 ResultSetHandler 接口来处理结果集。QueryRunner 类的常用方法如下图所示。

图片描述

ResultSetHandler 接口及其实现类

ResultSetHandler 接口用于处理 ResultSet 结果集,它可以将结果集中的数据封装成单个对象、数组、List、Map 等不同形式。

ResultSetHandler 接口有很多不同的实现类,如下图所示。

图片描述

这里我们介绍一下事务

在学习事务管理之前,我们有必要了解一下 ThreadLocal<T> 类。

ThreadLocal 可以为变量在每个线程中都创建一个副本,每个线程都可以访问自己内部的副本变量。因此,ThreadLocal 被称为线程本地变量(或线程本地存储)。

观察下面的代码,思考哪些地方可能会引发的线程安全问题?

public class ConnectionManager {
    private static Connection conn = null;
    public static Connection getConnection() throws /*…*/    {
        if(conn == null)    {
               conn = DriverManager.getConnection(/*...*/);
           }
           return conn;
    }

    public static void closeConnection() throws /*…*/{
        if(conn!=null)
            conn.close();
    }
}
复制代码

多线程场景下引发的问题分析:

  1. 因为 conn 是静态全局变量(用于共享),那么就有可能在一个线程使用 conn 操作数据库时,另外一个线程也同时调用 closeConnection() 关闭连接。
  2. 如果多个线程同时进入 getConnection() 方法的 if 语句,那么就会多次创建 conn 对象。

关于上述问题,你可能会想到使用“线程同步”来解决:将 conn 变量、getConnection() 和 closeConnection() 使用 synchronized 进行同步处理。以上面的代码为例,“线程同步”虽然可以解决问题,但会造成极大的性能影响:因为使用了线程同步后,当一个线程正在使用 conn 访问数据库时,其它线程只能等待。

以上面代码为例,引发线程安全问题的实质是因为 conn 变量、getConnection() 和 closeConnection() 都是共享的 static 变量(或方法)而造成的。实际上,一个线程只需要维护自己的 conn 变量,而不需要关心其它线程是否对各自的 conn 进行了修改。因此,不用 static 修饰也是可以的,如下:

public class ConnectionManager {
    // 没有 static 修饰
    private Connection conn = null;
    // 没有 static 修饰
    public Connection getConnection() throws SQLException {
        if (conn == null) {
            conn = DriverManager.getConnection(/*...*/);
        }
        return conn;
    }
    // 没有 static 修饰
    public void closeConnection() throws SQLException {
        if (conn != null)
            conn.close();
    }
}
class Dao{
       public void insert() throws SQLException {
           // 将 connectionManager 和 conn 定义为局部变量
        ConnectionManager connectionManager = new ConnectionManager();
        Connection conn = connectionManager.getConnection();
        // 使用 conn 访问数据库...
        connectionManager.closeConnection();
    }
}
复制代码

以上代码,将 conn 及相关方法的 static 修饰符去掉,然后在每个使用 conn 的方法中(如 insert())都创建局部变量 conn。这样一来,因为每次都是在方法内部创建的连接,那么线程之间自然不存在线程安全问题。但是,由于在方法中的 conn 变量会在方法结束时自动释放空间,因此频繁的方法调用就会频繁地开启和关闭数据库连接,从而严重影响程序执行性能。

ThreadLocal<T> 可以在每个线程中对该变量创建一个副本,即每个线程内部都会有一个该变量的副本,该副本在线程内部任何地方都可以共享使用,但不同线程的副本之间互不影响。

ThreadLocal<T> 类中有以下几个常用方法,如表所示:

方 法 简 介
public T get() 获取 ThreadLocal 在当前线程中保存的变量副本。
public void set(T value) 设置当前线程中变量的副本。
public void remove() 移除当前线程中变量的副本。
protected T initialValue() 延迟加载的方法,一般在使用时重写该方法。

连接池和 DbUtils 这个工具类主要有以下重点:

  • 数据库连接池可以分配、管理及释放数据库连接,可以使得应用程序重复地使用一个已有的数据库连接,而不再是重新建立一个。
  • 常用的数据源包括 Tomcat 内置数据源(Apache dbcp)、DBCP 数据源和 C3P0 数据源等,并且很多数据源都有基于配置文件和纯编码两种使用方式。
  • DbUtils 是一个工具类,提供了关闭连接、事务提交/回滚、注册 JDBC 驱动程序等常用方法。
  • QueryRunner 类主要用于执行增删改查等 SQL 语句。特别的,如果执行的是查询 SQL,还需要结合 ResultSetHandler 接口来处理结果集。
  • ResultSetHandler 接口用于处理 ResultSet 结果集,它可以将结果集中的数据封装成单个对象、数组、List 、Map 等不同形式。
  • ArrayHandler 类可以把结果集中的第一行数据封装成 Object[] 。
  • ArrayListHandler 类可以把结果集中的每行数据都封装成一个 Object[] 对象,然后再将所有的 Object[] 组装成一个 List 对象。
  • BeanHandler<T> 类可以把结果集中的第一行数据封装成 JavaBean 。
  • BeanListHandler<T> 类可以把结果集中的每行数据都封装成一个 JavaBean 对象,然后再将所有的 JavaBean 对象组装成一个 List 对象。
  • MapHandler 可以将结果集中的第一条数据封装到 Map 对象中,并且 key 是字段名,value 是字段值。
  • MapListHandler 可以将结果集中的每条数据都封装到 Map 对象中,然后再将所有的 Map 对象组装成一个 List 对象。
  • ColumnListHandler <T> 可以把结果集中某一列的值封装到 List 集合中。
  • ScalarHandler<T> 类用于封装单值结果。

图片描述

  • 使用 JNDI 定义的变量(通过 context.xml 中的 Environment 元素定义),可以在同一个 Tomcat 中的任意一个 Web 项目中使用。
  • 连接池减少连接创建时间。连接池中的连接已准备好的,可重复使用的,获取后可以直接使用访问数据库,因此减少了连接创建的次数和时间。
  • 连接池简化编程模式。当使用连接池时,每一个单独的线程能够创建一个自己的 JDBC 连接一样操作,允许用户直接使用 JDBC 编程技术。
  • 连接池控制资源的使用。如果不使用连接池,每次访问访问数据库都需要创建一个连接,这样系统的稳定性受系统连接需求影响很大,很容易产生资源浪费和高负载异常。连接池能够使用性能最大化,将资源利用控制在一定的水平之下。连接池能控制池中的连接数量,增强了新系统在大量用户应用时的稳定性。
  • ThreadLocal<T> 可以在每个线程中为变量创建一个副本,即每个线程内部都会有一个该变量的副本,该副本在线程内部任何地方都可以共享使用,但不同线程的副本之间互不影响。

写在最后

其实本文对连接池的理解还是比较片面,希望大家在看了以后还是好好深入理解一下。

Acho que você gosta

Origin juejin.im/post/7102609261204602893
Recomendado
Clasificación