传统的 DB 单例连接效果不佳
Posted
技术标签:
【中文标题】传统的 DB 单例连接效果不佳【英文标题】:Traditional DB singleton connection works poorly 【发布时间】:2013-12-21 06:21:33 【问题描述】:我在我的 java 应用程序中使用单例数据库连接,这是我的连接管理器类的代码:
public abstract class DatabaseManager
//Static instance of connection, only one will ever exist
private static Connection connection = null;
private static String dbName="SNfinal";
//Returns single instance of connection
public static Connection getConnection()
//If instance has not been created yet, create it
if(DatabaseManager.connection == null)
initConnection();
return DatabaseManager.connection;
//Gets JDBC connection instance
private static void initConnection()
try
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
String connectionUrl = "jdbc:sqlserver://localhost:1433;" +
"databaseName="+dbName+";integratedSecurity=true";
DatabaseManager.connection =
DriverManager.getConnection(connectionUrl);
catch (ClassNotFoundException e)
System.out.println(e.getMessage());
System.exit(0);
catch (SQLException e)
System.out.println(e.getMessage());
System.exit(0);
catch (Exception e)
public static ResultSet executeQuery(String SQL, String dbName)
ResultSet rset = null ;
try
Statement st = DatabaseManager.getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
rset = st.executeQuery(SQL);
//st.close();
catch (SQLException e)
System.out.println(e.getMessage());
System.exit(0);
return rset;
public static void executeUpdate(String SQL, String dbName)
try
Statement st = DatabaseManager.getConnection().createStatement();
st.executeUpdate(SQL);
st.close();
catch (SQLException e)
System.out.println(e.getMessage());
System.exit(0);
问题是我的代码在开始时运行良好,但是当时间过去时它变得非常慢。是什么导致了这个问题,我该如何解决? 在开始时,我的应用程序每秒处理大约 20 个查询,运行 1 小时后达到每秒 10 个查询,运行 3 天后达到每 10 秒 1 个查询! P.S:我的应用程序是一个单用户应用程序,它通过数据库进行许多查询。 P.S:这是我在 eclipse.ini 中的 JVM 参数:
--launcher.XXMaxPermSize
512M
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
512m
--launcher.defaultAction
openFile
--launcher.appendVmargs
-vmargs
-Dosgi.requiredJavaVersion=1.6
-Xms500m
-Xmx4G
-XX:MaxHeapSize=4500m
不幸的是数据库是远程的,我没有任何监控访问权限来了解那里发生了什么。
这是我的用法示例:
String count="select count(*) as counter from TSN";
ResultSet rscount=DatabaseManager.executeQuery(count, "SNfinal");
if(rscount.next())
numberofNodes=rscount.getInt("counter");
【问题讨论】:
在课堂上使用@singleton 即使对于单用户应用程序,我也建议使用连接池(例如 BoneCP)和一个非常小的池。只需获得一个连接并在完成后关闭它,池会处理检查连接,如果它已打开很长时间则替换它等。它还可以保护您的代码免受以前执行中未提交的事务等的影响。 实际上我需要尽可能快地运行查询。我第一次使用 c3p0,但它对我的应用程序的性能确实不如传统的单例连接(慢了大约 90%!) 为什么您的st.close()
被注释掉了?您是否正确关闭ResultSets
?当涉及到正确的资源处理时,JDBC 并不容易使用。我建议你使用像 spring-jdbc 和它的 JdbcTemplate
. 这样的包装库
你关于连接池很慢的论点似乎有点奇怪。当涉及到数据库通信时,一些方法调用开销是微不足道的。但我同意,如果您的用例不需要连接池,则不需要它。您可能缺少的唯一优势是连接验证。
【参考方案1】:
是什么导致了这个问题,我该如何解决?
您在这里遇到的主要问题是executeQuery()
方法。
你没有关闭Statement
,我想你已经评论了st.close()
这一行,因为你需要打开ResultSet
用于进一步处理。
我可以看到您的想法是避免在您的应用程序中看到重复的 JDBC 代码,但这不是正确的方法。
规则是:关闭ResultSet
,然后关闭Statement
,
否则,您将无法正确释放资源,并且会遇到您所描述的问题。
Here 你可以找到关于如何正确关闭资源的很好的解释(请记住,在你的情况下你不需要 关闭连接)
编辑: 一个例子可以是
try
Statement st = DatabaseManager.getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
ResultSet rsCount = st.executeQuery(count); //count="select count(*) as counter from TSN";
if(rsCount.next())
numberofNodes=rscount.getInt("counter");
catch (SQLException e)
//log exception
finally
rsCount.close();
st.close();
【讨论】:
我无法关闭结果集或语句,因为我要运行多个查询。我一关闭它们就出错了。 分配的内存没有满,所以不可能是内存问题。我认为它是由其他原因引起的,但我不知道是什么。 我坚持认为您的 executeQuery() 方法设计得不好。尝试进行一些重构以允许您关闭该语句。我不知道您的应用程序代码,但它应该很容易替换每个 DatabaseManager.executeQuery() 方法调用,用于允许您关闭的树语句(1-> 获取连接、2-> 创建语句、3-> 获取结果集) ResultSet,尤其是 Statement。 我添加了一个如何调用此方法的示例,请您解释一下您对这个示例的想法吗? 在我的所有代码中更改它确实非常耗时,因此我选择了 Summit 解决方案。我想知道为什么该解决方案完全解决了我的问题!【参考方案2】:您应该考虑使用断开连接的结果集,例如 CachedRowSet http://docs.oracle.com/javase/1.5.0/docs/api/javax/sql/rowset/CachedRowSet.html
public static ResultSet executeQuery(String SQL, String dbName)
CachedRowSetImpl crs = new CachedRowSetImpl();
ResultSet rset = null ;
Statement st = null;
try
st = DatabaseManager.getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
rset = st.executeQuery(SQL);
crs.populate(rset);
catch (SQLException e)
System.out.println(e.getMessage());
System.exit(0);
finally
rset.close();
st.close();
return crs;
CachedRowSet 实现了 ResultSet,所以它的行为应该像一个 ResultSet。
http://www.onjava.com/pub/a/onjava/2004/06/23/cachedrowset.html
除了这些更改之外,我建议您使用池数据源来获取连接并关闭它们,而不是保持一个打开的连接。
http://brettwooldridge.github.io/HikariCP/
或者,如果您不是 java7、bonecp 或 c3po。
编辑:
为了回答您的问题,这解决了您的问题,因为 CachedRowSetImpl
在使用时不会保持与数据库的连接。
这允许您在填充CachedRowSetImpl
后关闭Resultset
和Statement
。
希望能回答您的问题。
【讨论】:
请问为什么这个解决方案解决了我的问题?!我在这两种方法中都监控了 jvm 资源的使用情况。从资源管理的角度来看,似乎默认方法更好,所以不会是资源使用率高造成的!【参考方案3】:虽然连接管理器会自动关闭Statement
和Resultset
,但最好立即关闭。
您的代码中没有任何其他内容会影响您的单线程任务,所以我敢打赌您的数据库中一定有问题。尝试找出是否有任何数据库锁定或错误的列索引。还要看看数据库查询状态,找出瓶颈在哪里。
【讨论】:
如果是数据库问题,为什么要随着时间的推移而延长? 因为你的数据集一直在增长,所以查询 100M 记录肯定比 100K 记录要慢。 不,它没有增长,我逐条记录查询过程!以上是关于传统的 DB 单例连接效果不佳的主要内容,如果未能解决你的问题,请参考以下文章
Try/Catch 在调试中效果很好,但在 .exe 中效果不佳