我发现当多个线程从ComboPooledDataSource的单个共享实例请求连接时,有时会从已经在使用中的池中返回连接。是否有配置设置或其他方法来确保当前检出的连接不会再次签出?
package stress;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mchange.v2.c3p0.DataSources;
public class StressTestDriver
{
private static final String _host = "";
private static final String _port = "3306";
private static final String _database = "";
private static final String _user = "";
private static final String _pass = "";
public static void main(String[] args)
{
new StressTestDriver();
}
StressTestDriver()
{
ComboPooledDataSource cpds = new ComboPooledDataSource();
try
{
cpds.setDriverClass( "com.mysql.jdbc.Driver" );
String connectionString = "jdbc:mysql://" + _host + ":" + _port + "/"
+ _database;
cpds.setJdbcUrl( connectionString );
cpds.setMaxPoolSize( 15 );
cpds.setMaxIdleTime( 100 );
cpds.setAcquireRetryAttempts( 1 );
cpds.setNumHelperThreads( 3 );
cpds.setUser( _user );
cpds.setPassword( _pass );
}
catch( PropertyVetoException e )
{
e.printStackTrace();
return;
}
write("BEGIN");
try
{
for(int i=0; i<100000; ++i)
doConnection( cpds );
}
catch( Exception ex )
{
ex.printStackTrace();
}
finally
{
try
{
DataSources.destroy( cpds );
}
catch( SQLException e )
{
e.printStackTrace();
}
}
write("END");
}
void doConnection( final ComboPooledDataSource cpds )
{
Thread[] threads = new Thread[ 10 ];
final Set<String> set = new HashSet<String>(threads.length);
Runnable runnable = new Runnable()
{
public void run()
{
Connection conn = null;
try
{
conn = cpds.getConnection();
synchronized(set)
{
String toString = conn.toString();
if( set.contains( toString ) )
write("In-use connection: " + toString);
else
set.add( toString );
}
conn.close();
}
catch( Exception e )
{
e.printStackTrace();
return;
}
}
};
for(int i=0; i<threads.length; ++i)
{
threads[i] = new Thread( runnable );
threads[i].start();
}
for(int i=0; i<threads.length; ++i)
{
try
{
threads[i].join();
}
catch( InterruptedException e )
{
e.printStackTrace();
}
}
}
private static void write(String msg)
{
String threadName = Thread.currentThread().getName();
System.err.println(threadName + ": " + msg);
}
}
答案 0 :(得分:0)
我在我的环境中运行压力测试。它在没有输出的情况下终止。
也就是说,使用toString()输出作为身份的标记对我来说有点不可靠。在Object.toString()中编码的哈希码不保证是唯一的,并且您正在生成批量。你可能只是看到了碰撞。
特别是,您关注的场景将代表一种令人惊讶且令人困惑的错误。您应该看到com.mchange.v2.c3p0.impl.NewProxyConnection的实例。这些实例不会在池中签入和签出 - 它们是一次性使用,包装底层数据库Connection的一次性代理。当你调用getConnection()时,它们用new构造,在你工作时保持与PooledConnection对象相关联,然后当你调用close()时由c3p0库取消引用,在客户端代码取消引用后进行垃圾回收。如果以某种方式在同一个底层PooledConnection上调用getConnection(),那将是一个c3p0错误,而c3p0会发出警告。你没有看到任何像......
你是吗?c3p0 - 呃哦...当PooledConnection调用getConnection()时 它已经为客户端提供了尚未安装的连接 已经关闭。这可能表示连接池中存在错误!!!
我将验证您是否只是看到NewProxyConnection.toString()的冲突。使用映射而不是集合,保持对close()ed连接实例的引用作为值,当您看到String键的冲突时,请与==
一起检查新Connection是否与您的值中的值相同地图。如果你发现它们是同一个实例,我会感到很惊讶。 (我希望我能重现这个问题,以便我自己验证这一点。)
答案 1 :(得分:0)
我添加了代码以获取Oracle和MS SQL Server的连接ID / @@ SPID,并且每当toString值相同时,连接ID总是不同的,因此c3p0不是罪魁祸首:
94843 - pool-1-thread-12 - :onCheckOut调用:com.microsoft.sqlserver.jdbc.SQLServerConnection@5e65d9 - H:6186457 - P :: 57
94843 - pool-1-thread-13 - :onCheckOut调用:com.microsoft.sqlserver.jdbc.SQLServerConnection@170a25e - H:24158814 - P :: 55
94843 - pool-1-thread-1 - :onCheckOut调用:com.microsoft.sqlserver.jdbc.SQLServerConnection@5e65d9 - H:6186457 - P :: 54
19172 - pool-1-thread-11 - :onCheckOut调用:oracle.jdbc.driver.T4CConnection@2475ca - H:2389450 - P :: 6564
19172 - pool-1-thread-31 - :onCheckOut调用:oracle.jdbc.driver.T4CConnection@2475ca - H:2389450 - P :: 6448
我将不得不考虑修改现有代码。谢谢你的帮助史蒂夫!你指出了我正确的方向。