我将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"
/>
答案 0 :(得分:1)
使用游泳池时,方式#2不正确。如果您使用池,则应始终尝试将连接保持在池外(&#34;租用&#34;)尽可能短,以便最大限度地利用池。如果不使用池,则必须考虑创建和销毁连接以及管理连接生命周期的成本。
如果使用池,则必须始终将租用的连接返回到池(关闭连接时会将连接返回到池)。如果没有将连接返回到池中(即连接被泄漏),则池很快将为空,您的应用程序将停止工作。当出现问题时(例如在您的代码示例中,由于查询错误导致rs
为null
时,连接将被泄露),这一点尤其重要。为防止连接泄漏,请考虑使用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上)以及许多其他因素。