JDBC池 - 如何正确使用

时间:2017-03-23 19:00:23

标签: java tomcat jdbc

我将Tompool用于我的Tomcat Web应用程序。

在我阅读文档时,我需要获取连接,执行查询和关闭。 关闭后,连接返回池。

我认为,当查询数据库依赖于外部事件时,方法1更好,而且每隔5秒运行2的任务更正确。

有人可以解释哪个方法用于每5秒重复一次的任务?

PS:我在代码中跳过了额外的检查,以使代码看起来可读。

方式#1 从泳池获取连接并每5秒关闭一次

Connection c = null;
Statement s = null;
ResultSet rs = null;
DataSource ds = ... Get DataSource  ...

while(running) {
    try {
        c = ds.getConnection();
        s = c.createStatement();
        rs = s.executeQuery('SELECT data FROM my_table');
        ... do something with result ...
    } catch (SQLException sec) {
        ... print exception ...
    } finally {
        try {
            rs.close();
            s.close();
            c.close();
        } catch (SQLException sec) { ... print exception ... }

        ... Thread sleep 5 seconds and repeat ...
    }
}

方式#2 在循环之前获取连接并在之后关闭,重新连接内部循环

Connection c = null;
Statement s = null;
ResultSet rs = null;
DataSource ds = ... Get DataSource  ...

c = ds.getConnection();

while(running) {
    try {
        s = c.createStatement();
        rs = s.executeQuery('SELECT data FROM my_table');
        ... do something with result ...
    } catch (SQLException sec) {
        ... print exception ...
        ... if connection lost, try reconnect and execute query again ...
    } finally {
        try {
            rs.close();
            s.close();
        } catch (SQLException sec) {
            ... print exception ...
        }
        ... Thread sleep 5 seconds and repeat ...
    }
}

c.close();

游泳池配置

<Resource name="jdbc/pg_mega" auth="Container"
        type="javax.sql.DataSource" driverClassName="org.postgresql.Driver"
        url="jdbc:postgresql://127.0.0.1:6432/db"
        factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
        username="***" password="****"
        defaultAutoCommit="true"
        initialSize="1"
        maxActive="300"
        maxTotal="300"
        maxIdle="20"
        minIdle="5"
        maxWait="10000"
        validationQuery="select 1"
        validationInterval="30000"
        testWhileIdle="false"
        testOnBorrow="true"
        testOnReturn="false"
        timeBetweenEvictionRunsMillis="30000"
        minEvictableIdleTimeMillis="30000"
        />

2 个答案:

答案 0 :(得分:1)

使用游泳池时,方式#2不正确。如果您使用池,则应始终尝试将连接保持在池外(&#34;租用&#34;)尽可能短,以便最大限度地利用池。如果不使用池,则必须考虑创建和销毁连接以及管理连接生命周期的成本。

如果使用池,则必须始终将租用的连接返回到池(关闭连接时会将连接返回到池)。如果没有将连接返回到池中(即连接被泄漏),则池很快将为空,您的应用程序将停止工作。当出现问题时(例如在您的代码示例中,由于查询错误导致rsnull时,连接将被泄露),这一点尤其重要。为防止连接泄漏,请考虑使用Sql2o之类的工具,该工具具有内置的连接泄漏保护功能。

还减少池中的连接数。从minIndle="1"maxActive="4"开始。使用压力测试来确定池大小的上限(池中的更多连接通常弊大于利),另请参阅About Pool Sizing中的HikariCP,其中包含有关数据库连接池的更多好文章)。

答案 1 :(得分:1)

我认为最常见的模式是:

    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet res = null;
    try {
        conn = ds.getConnection();
        stmt = conn.prepareStatement(sqlStatement);
        //
        // ....
        res = stmt.executeQuery();

        // use the resultset

        conn.commit();
    } catch (SQLException e) {
        // Manage the exception
        try {
            conn.rollback();
        } catch (SQLException e1) {
            // SWALLOW
        }

    } finally {
        close(res);
        close(stmt);
        close(conn);
    }

我使用这些辅助函数安全地关闭而没有太多样板文件,从java 7开始你就可以自动关闭,所以这些帮助程序不再用了。

public static void close(Connection conn) {
    try {
        if (conn != null)
            conn.close();
    } catch (SQLException e) {
        // SWALLOW
    }
}

public static void close(Statement stmt) {
    try {
        if (stmt != null)

            stmt.close();
    } catch (SQLException e) {
        // SWALLOW
    }
}

public static void close(ResultSet res) {
    try {
        if (res != null)
            res.close();
    } catch (SQLException e) {
        // SWALLOW
    }
}

你应该确定在finally语句中关闭连接,如果你没有关闭连接会发生坏事,并且如果(例如)rs在你的例子中为空(不是那么困难)你就不会关闭连接。

从池中获取和释放连接不是性能问题,它在几微秒内发生,比任何可能的查询快数千倍。

您不急切地释放连接的原因是事务,您希望为整个事务保持相同的连接(没有办法解决此问题)。

当您提交(或回滚)时,您不再需要那种特殊的连接,所以只需将其释放即可。

另一个提示,最后关闭连接,即使你捕获SQL Exceptions,因为总有Runtime Exceptions甚至是Errors(你都不会捕获),但是最终甚至会在OutOfMemoryError或ClassDefNotFoundError或者任何其他,连接将返回池。

最后但并非最不重要的是在断开连接时尝试重新连接的池,实际上池只会丢弃无效连接并在需要时创建一个新的批处理。

你应该选择一个良好的连接验证策略,一个糟糕的选择将导致额外的时间获得连接,从而对性能造成严重影响,或者导致从池中获取的无效连接导致的异常。

优化池就像许多其他性能调整任务一样: HARD

例如: testOnBorrow="true"会在获取连接之前命中数据库,这是安全的,但是如果没有检查借用,则会花费数十或数百倍的时间。

testWhileIdle="true"相反不太安全(你可能会得到一个无效的连接)但速度要快得多,这样可以保持连接的存在。

您必须选择考虑如何使用连接,如何处理错误,DB在哪里(在同一台机器上,在局域网上,在wan上)以及许多其他因素。