0

我发现当多个线程分别从 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);
    }
}
4

2 回答 2

0

我已经在我的环境中对你进行了压力测试。它没有输出就终止了。

也就是说,使用 toString() 输出作为身份标记让我觉得有点不可靠。在 Object.toString() 中编码的哈希码不能保证唯一,并且您正在生成很多。你可能只是看到碰撞。

特别是,您所关注的场景将代表一种令人惊讶和令人困惑的错误。您应该看到 com.mchange.v2.c3p0.impl.NewProxyConnection 的实例。这些实例不会在池中签入和签出 - 它们是一次性使用的一次性代理,用于包装底层数据库连接。它们在您调用 getConnection() 时使用 new 构造,在您工作时与 PooledConnection 对象保持关联,然后在您调用 close() 时由 c3p0 库取消引用,在客户端代码取消引用它们后被垃圾收集。如果在同一个底层 PooledConnection 上以某种方式调用 getConnection(),那将是一个 c3p0 错误,并且 c3p0 会发出一个警告。你没有看到类似...

c3p0 -- 哦哦......当它已经为客户端提供了一个尚未关闭的连接时,它在 PooledConnection 上调用了 getConnection()。这可能表明连接池中存在错误!

你是?

我会验证您不只是看到 NewProxyConnection.toString() 的冲突。使用映射而不是集合,将 close()ed Connection 实例的引用保存为值,当您看到字符串键的冲突时,检查==新的 Connection 是否与映射中的值相同。如果你发现它们是同一个实例,我会感到惊讶。(我希望我可以重现该问题以自行验证。)

于 2013-09-11T09:11:37.550 回答
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

我将不得不考虑修改现有代码。感谢您的帮助史蒂夫!你为我指明了正确的方向。

于 2013-09-12T00:31:07.323 回答