Jdbc 从ComboPooledDataSource获取的连接偶尔已签出
我发现,当多个线程分别从ComboPooledDataSource的单个共享实例请求连接时,可能会从已经使用的池返回连接。是否有配置设置或其他方法确保当前签出的连接不会再次签出Jdbc 从ComboPooledDataSource获取的连接偶尔已签出,jdbc,c3p0,Jdbc,C3p0,我发现,当多个线程分别从ComboPooledDataSource的单个共享实例请求连接时,可能会从已经使用的池返回连接。是否有配置设置或其他方法确保当前签出的连接不会再次签出 package stress; import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.SQLException; import java.util.HashSet; import java.util.Se
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);
}
}
包装应力;
导入java.beans.PropertyVetoException;
导入java.sql.Connection;
导入java.sql.SQLException;
导入java.util.HashSet;
导入java.util.Set;
导入com.mchange.v2.c3p0.ComboPooledDataSource;
导入com.mchange.v2.c3p0.DataSources;
公共类测试驱动程序
{
私有静态最终字符串_host=“”;
专用静态最终字符串_port=“3306”;
私有静态最终字符串_database=“”;
私有静态最终字符串_user=“”;
私有静态最终字符串_pass=“”;
公共静态void main(字符串[]args)
{
新的StressTestDriver();
}
StressTestDriver()
{
ComboPooledDataSource cpds=新ComboPooledDataSource();
尝试
{
setDriverClass(“com.mysql.jdbc.Driver”);
String connectionString=“jdbc:mysql://”+\u主机+:“+\u端口+”/”
+_数据库;
setJdbcUrl(connectionString);
设置最大池大小(15);
设置最大空闲时间(100);
cpds.SetAcquisitioneTryAttents(1);
setNumHelperThreads(3);
cpds.setUser(_user);
设置密码(_pass);
}
捕获(PropertyVetOe异常)
{
e、 printStackTrace();
返回;
}
写(“开始”);
尝试
{
对于(int i=0;i我已经在我的环境中运行了you压力测试。它在没有输出的情况下终止
也就是说,我觉得使用toString()输出作为标识的令牌有点不可靠。Object.toString()中编码的哈希代码不保证唯一,并且您正在生成大量的哈希代码。您可能只是看到了冲突
特别是,您所关心的场景可能代表一种令人惊讶和困惑的错误。您应该看到com.mchange.v2.c3p0.impl.NewProxyConnection的实例。这些实例没有签入和签出池-它们是包装底层数据库连接的一次性代理。它们是缺点在调用getConnection()时使用new命令,在工作时保持与PooledConnection对象的关联,然后在调用close()时被c3p0库取消引用,以便在客户端代码取消引用后进行垃圾收集。如果在同一个基础PooledConnection上被调用,这将是一个c3p0错误,c3p0将发出一个警告
c3p0--噢…在以下情况下对PooledConnection调用了getConnection()
它已经为客户端提供了一个尚未连接的连接
已关闭。这可能表示连接池中存在错误
是吗
我将验证您是否只看到NewProxyConnection.toString()的冲突。请使用映射而不是集合,保留对close()的引用将连接实例设置为值,当您看到字符串键发生冲突时,请使用==
检查新连接是否与映射中的值相同。如果您发现它们是相同的实例,我会感到惊讶。(我希望我能够重现此问题,以自己验证这一点。)我添加了代码以获取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-:使用com.microsoft.sqlserver.jdbc调用onCheckOut。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
我必须研究修改现有代码。感谢您的帮助Steve!您为我指明了正确的方向。感谢您的响应。我考虑了哈希代码冲突的可能性,并且创建了许多。但是,请记住,我一次创建10个连接。DoConnection调用100000次,但每次调用从新的HashSet实例开始,最大容量为10个元素。这意味着冲突发生在10个对象的空间中。连接对象conn确实是com.mchange.v2.c3p0.impl.NewProxyConnection的实例。我没有看到您提到的警告。我在我的pr中实现了ConnectionCustomizer用于显示未加密连接信息的生产代码:21921-com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#0-:onAcquire,使用:oracle.jdbc.driver调用。T4CConnection@141e41d:H:21095453 21921-pool-1-thread-6-:onCheckOut调用时使用:oracle.jdbc.driver。T4CConnection@141e41d:H:21095453 21921-com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#1-:onAcquire调用时使用:oracle.jdbc.driver。T4CConnection@141e41d:H:21095453 21921-pool-1-thread-29-:onCheckOut使用:oracle.jdbc.driver调用。T4CConnection@141e41d:H:21095453第一列是自运行开始以来的毫秒数。第二列是线程数e、 接下来是ConnectionCustomizer方法名([onAcquire,onCheckOut])。接下来是conn.toString()调用