天天看點

資料庫連接配接池 - (druid、c3p0、dbcp)

一、概述

在這裡所謂的資料庫連接配接是指通過網絡協定與資料庫服務之間建立的TCP連接配接。通常,與資料庫服務進行通信的網絡協定無需由應用程式本身實作。

原因有三:

  1. 實作複雜度大,需要充分了解和掌握相應的通信協定。
  2. 代碼難以複用,每個應用程式都需要獨立實作一套對應的網絡協定(不同公司之間,同一公司的不同技術棧之間難以複用實作相同協定的代碼)
  3. 性能難以保證,不同的網絡協定實作可能存在巨大的性能差距。

正因為如此,是以現實的實作方式是:

首先,定義網絡協定标準,這樣隻要支援這個标準協定的資料庫就可以使用相應的用戶端與之通信。

其次,将實作這個标準協定的用戶端獨立為一個通信庫,這樣隻需要在應用程式中使用這個通信元件庫就可以友善地實作與資料庫進行互動。

通常,我們将實作了網絡協定的通信庫稱之為資料庫驅動程式。當然,對于不同的程式設計語言,需要對應編寫相應的資料庫驅動實作。以與關系型資料庫通信為例,在Java中實作的驅動程式為JDBC,Python中的驅動程式為MySQLdb。

由于通過TCP與資料庫建立網絡連接配接的代價非常高昂,而且耗時(TCP建立連接配接需要“三次握手”,斷開連接配接需要“四次握手”)。是以在實踐中通常不直接單獨使用連接配接進行資料庫操作,而是使用連接配接池的方式,這主要是處于以下兩方面的考慮:

  1. 應用程式本身需要更低的響應時間,如果每次資料庫操作都需要經過“建立連接配接->通信(增删改查)->斷開連接配接”這個過程,那麼勢必會導緻響應延時的增加。
  2. 避免伺服器資源被耗盡,随着業務量的增大,對應的資料庫操作必然會随之增加,如果對用戶端的連接配接數不加以控制,可能會導緻資料庫伺服器的CPU和記憶體資源被大量的網絡連接配接快速耗盡,這樣将導緻服務不可用。
資料庫連接配接池 - (druid、c3p0、dbcp)

在Java中使用得比較流行的資料庫連接配接池主要有:DBCP,c3p0,druid。

另外,不論使用什麼連接配接池,低層都是使用JDBC連接配接,即:在應用程式中都需要加載JDBC驅動程式。

二、Druid

1)概述

druid 阿裡出品,淘寶和支付寶專用資料庫連接配接池,支援所有JDBC相容的資料庫,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等等。

Druid是Java語言中最好的資料庫連接配接池,Druid能夠提供強大的監控和擴充功能,是一個可用于大資料實時查詢和分析的高容錯、高性能的開源分布式系統,尤其是當發生代碼部署、機器故障以及其他産品系統遇到當機等情況時,Druid仍能夠保持100%正常運作。

2)主要特色

為分析監控設計;快速的互動式查詢;高可用;可擴充;Druid是一個開源項目,源碼托管在github上。

Druid針對Oracle和MySql做了特别優化。

3)代碼示範

依賴Jar包:不分版本号

● druid-1.1.5.jar

● mysql-connector-java-8.0.14.jar

DruidDataSource dataSource = new DruidDataSource();
//擷取驅動
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
//建立連接配接
dataSource.setUrl("jdbc:mysql://localhost:3306/class38?serverTimezone=Asia/Shanghai");
dataSource.setUsername("root");
dataSource.setPassword("123456");
try {
    //擷取連接配接
    DruidPooledConnection conn = dataSource.getConnection();
    PreparedStatement statement = conn.prepareStatement("insert into student values(?,?,?,?)");
    statement.setInt(1, 13);
    statement.setString(2, "小明");
    statement.setString(3, "資料庫");
    statement.setInt(4, 150);
    int i = statement.executeUpdate();
    System.out.println(i);
} catch (SQLException e) {
    e.printStackTrace();
}      

設定配置檔案:druid.properties

driverClassName = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/class38?serverTimezone=Asia/Shanghai
username = root
password = 123456      

通過配置檔案實作連接配接:

//建立工廠
DruidDataSourceFactory factory = new DruidDataSourceFactory();
Properties p = new Properties();
InputStream in = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
try {
    //讀取配置檔案中的資訊
    p.load(in);
    DataSource dataSource = factory.createDataSource(p);
    Connection conn = dataSource.getConnection();
    PreparedStatement statement = conn.prepareStatement("select * from student");
    ResultSet result = statement.executeQuery();
    while(result.next()){
        System.out.println(result.getInt(1)+","+result.getString(2)+","+result.getString(3)+","+result.getInt(4));
    }
} catch (Exception e) {
    e.printStackTrace();
}      

三、C3P0

開源的JDBC連接配接池,實作了資料源和JNDI綁定,支援JDBC3規範和JDBC2的标準擴充。目前使用它的開源項目有Hibernate、Spring等。

資料庫連接配接池C3P0架構是個非常優異的開源jar,高性能的管理着資料源,c3p0有兩種資料源管理方式,一種是通過程式變本身來進行管理,還有一種是通過容器管理,

c3p0有自動回收空閑連接配接功能。單線程,性能較差,适用于小型系統,代碼600KB左右。

2)代碼示範

● c3p0-0.9.5.2.jar

● mchange-commons-java-0.2.11.jar

① 連接配接使用C3P0

1.1 - 使用Java API方式配置c3p0

ComboPooledDataSource dataSource = new ComboPooledDataSource();
    try {
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/class38?serverTimezone=Asia/Shanghai");
        dataSource.setUser("root");
        dataSource.setPassword("123456");
        Connection conn = dataSource.getConnection();
        PreparedStatement statement = conn.prepareStatement("delete from student where id=?");
        statement.setInt(1, 11);
        int i = statement.executeUpdate();
        System.out.println(i);
    } catch (Exception e) {
        e.printStackTrace();
    }
}      

1.2 - 直接在連接配接池中擷取連接配接

DataSource ds = new ComboPooledDataSource();/
try {
    Connection connection = ds.getConnection();
    String sql = "select * from goods";
    PreparedStatement prepareStatement = connection.prepareStatement(sql);// 設定sql語句
    ResultSet executeQuery = prepareStatement.executeQuery();// 運作
    while (executeQuery.next()) {
        System.out.println("商品:" + executeQuery.getObject(2).toString());
    }
    executeQuery.close();
    prepareStatement.close();
    connection.close();
} catch (SQLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}      

★1.3 - 使用DBUtils工具連接配接:需要配置XML資料源,使用工具連接配接不需要close

//擷取資料源,讀取的是C3P0-config預設配置建立資料庫連接配接池對象
DataSource ds = new ComboPooledDataSource();
//建立QueryRunner帶參對象
QueryRunner queryRunner = new QueryRunner(ds);
//SQL UPDATE語句
String sql = "DELETE FROM type WHERE id in(?,?)";
//執行UPDATE語句,傳回一個int類型的結果
int update = queryRunner.update(sql,6,7);
System.out.println(update);      

② 使用c3p0.properties檔案進行配置

資料源配置:c3p0.properties

需要在classpath路徑下添加配置檔案:c3p0.properties,内容如下:

c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql://host:port/db
c3p0.user=root
c3p0.password=
c3p0.minPoolSize=5
c3p0.maxPoolSize=20
c3p0.acquireIncrement=5      

在應用程式中隻需要直接建立ComboPooledDataSource對象即可(c3p0會自動從classpath加載c3p0.properties中的配置資訊):

ComboPooledDataSource cpds = new ComboPooledDataSource();
Connection conn = cpds.getConnection();
query(conn);
cpds.close();      

注意: 使用c3p0.properties作為配置檔案時,每個參數的name字首必須是“c3p0”,如:“c3p0.driverClass=com.mysql.jdbc.Driver”。

③ 使用c3p0-config.xml檔案進行配置

資料源配置:c3p0-config.xml

使用這種方式會比使用c3p0.properties更加進階,支援配置多個資料源,同樣需要在classpath路徑下添加檔案:c3p0-config.xml

<c3p0-config>
    <!-- 這是預設配置資訊 -->
    <default-config>
        <!-- 連接配接四大參數配置 -->
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl"><!--CDATA裡面的特殊符号都當字元串處理-->
            <![CDATA[jdbc:mysql://localhost:3306/cakeshop?useUnicode=true&character=UTF-8&serverTimezone=GMT%2B8&useSSL=true]]>
        </property>
        <property name="user">使用者名</property>
        <property name="password">密碼</property>
        <!-- 池參數配置 -->
        <property name="acquireIncrement">3</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">2</property>
        <property name="maxPoolSize">10</property>
    </default-config>
</c3p0-config>      

 在應用程式中隻需要直接建立ComboPooledDataSource對象即可(c3p0會自動從classpath加載c3p0-config.xml中的配置資訊):

// 使用預設資料源
// ComboPooledDataSource cpds = new ComboPooledDataSource();

// 使用指定名稱的資料源
ComboPooledDataSource cpds = new ComboPooledDataSource("myDataSource");
Connection conn = cpds.getConnection();
query(conn);
cpds.close();      

四、DBCP

由Apache開發的一個Java資料庫連接配接池項目, Jakarta commons-pool對象池機制,Tomcat使用的連接配接池元件就是DBCP。

單獨使用dbcp需要3個包,預先将資料庫連接配接放在記憶體中,應用程式需要建立資料庫連接配接時直接到連接配接池中申請一個就行,用完再放回。

單線程,并發量低,性能不好,适用于小型系統。

● commons-dbcp2-2.4.0.jar

● commons-pool2-2.6.2.jar

● commons-logging-1.2.jar

//建立連接配接池對象
BasicDataSource dataSource = new BasicDataSource();
//設定參數
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/class38?serverTimezone=Asia/Shanghai");
dataSource.setUsername("root");
dataSource.setPassword("123456");
try {
    //擷取連接配接對象
    Connection conn = dataSource.getConnection();
    //生成預編譯Statement對象
    PreparedStatement statement = conn.prepareStatement("insert into student values(null,?,?,?)");
    statement.setString(1, "帥帥");
    statement.setString(2, "大資料");
    statement.setInt(3, 100);
    //執行sql語句
    int i = statement.executeUpdate();
    System.out.println(i);
} catch (Exception e) {
    e.printStackTrace();
}      

配置檔案内容:dbcp.properties

driverClassName = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/class38?serverTimezone=Asia/Shanghai
username = root
password = 123456      

通過Properties配置檔案連接配接:

BasicDataSourceFactory factory = new BasicDataSourceFactory();
Properties p = new Properties();
try {
    InputStream in = DBCPDemo.class.getClassLoader().getResourceAsStream("dbcp.properties");
    //加載配置檔案
    p.load(in);
    //建立一個對象并傳回
    DataSource dataSource = factory.createDataSource(p);
    Connection conn = dataSource.getConnection();
    PreparedStatement statement = conn.prepareStatement("select * from student");
    ResultSet result = statement.executeQuery();
    while(result.next()){
        System.out.println(result.getInt(1)+","+result.getString(2)+","+result.getString(3)+","+result.getInt(4));
    }
} catch (Exception e) {
    e.printStackTrace();
}      

@ 轉載至網絡