Jdbc 从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

我发现,当多个线程分别从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);
    }
}
包装应力;
导入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()调用