基于Java 实现测试 C3P0 在 Oracle 数据库上的性能和参数,并动态监控各项参数指标。
一. C3P0常用参数
参数名称 | 解释 | 优化建议 |
---|---|---|
initialPoolSize | 初始化时创建的连接数。影响应用启动时的连接准备速度。 | 根据启动初期负载设置,建议与 minPoolSize 相同,如 5 。 |
minPoolSize | 连接池中的最小连接数。低于该值时会补充新连接。 | 设置为常规最低负载下的并发连接数,如 5 。 |
maxPoolSize | 最大连接数,限制同时连接的数量。 | 根据数据库性能和硬件资源限制,推荐 CPU 核心数 * 2 或更高。 |
maxIdleTime | 连接的最大空闲时间,超时的连接会被关闭。 | 设置为 300 秒,避免无用连接占用资源。 |
acquireIncrement | 每次需要新连接时一次性创建的连接数。 | 设置为 5 ,避免频繁创建单个连接带来的性能开销。 |
checkoutTimeout | 获取连接时的最大等待时间,超过该时间则抛出异常。 | 设置为 3000 毫秒,防止客户端长时间阻塞。 |
idleConnectionTestPeriod | 闲置连接测试周期,周期性测试连接是否有效。 | 推荐设置为 60 秒,确保空闲连接不会因网络问题变为无效连接。 |
testConnectionOnCheckin | 当连接返回池时是否测试其有效性 | 开启为 true ,提高连接池的可靠性。 |
testConnectionOnCheckout | 从池取连接时是否测试连接的有效性 | 建议关闭为 false ,以提升性能。如果需要提高稳定性,则可以设置为true |
acquireRetryAttempts | 获取连接失败时重试的次数 | 设置为 3。 |
acquireRetryDelay | 获取连接失败后重试的间隔时间 | 设置为 1000 毫秒。 |
maxStatements | 缓存的 SQL 语句的总数 | 根据实际情况设置,例如 100。 |
maxStatementsPerConnection | 每个连接的最大缓存 SQL 语句数 | 设置为 20,结合 maxStatements 合理分配。 |
numHelperThreads | C3P0 使用的辅助线程数 | 默认 3,可以根据 CPU 核数适当提高,例如 6。 |
二. Java 测试代码
Maven 依赖
<dependencies>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>19.14.0.0</version>
</dependency>
</dependencies>
具体实现代码
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class C3P0PerformanceTest {
// 配置数据库信息
private static final String ORACLE_URL = "jdbc:oracle:thin:@oracle_host:1521:dbname";
private static final String ORACLE_USER = "user";
private static final String ORACLE_PASSWORD = "password";
// 数据源
private static ComboPooledDataSource dataSource;
public static void main(String[] args) {
try {
// 初始化数据源
dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("oracle.jdbc.OracleDriver");
dataSource.setJdbcUrl(ORACLE_URL);
dataSource.setUser(ORACLE_USER);
dataSource.setPassword(ORACLE_PASSWORD);
// 性能相关的核心参数
dataSource.setInitialPoolSize(10); // 初始连接池大小
dataSource.setMinPoolSize(5); // 最小连接池大小
dataSource.setMaxPoolSize(50); // 最大连接池大小
dataSource.setAcquireIncrement(5); // 每次需要新连接时增加的连接数
dataSource.setMaxIdleTime(300); // 连接最大空闲时间(秒)
dataSource.setIdleConnectionTestPeriod(60); // 检查空闲连接的时间间隔(秒)
// 高级参数配置
dataSource.setTestConnectionOnCheckin(true); // 在连接返回池时检查是否有效
dataSource.setTestConnectionOnCheckout(false); // 禁用取出连接时的检查以提高性能
dataSource.setAcquireRetryAttempts(3); // 获取连接失败时的重试次数
dataSource.setAcquireRetryDelay(1000); // 重试间隔时间(毫秒)
dataSource.setMaxStatements(100); // PreparedStatement 缓存大小
dataSource.setMaxStatementsPerConnection(20); // 每个连接的最大缓存语句数
dataSource.setNumHelperThreads(6); // 帮助线程池大小,处理慢任务
// 启动性能测试线程池
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executor.submit(() -> executeQuery("SELECT COUNT(*) FROM YOUR_TABLE"));
}
// 启动动态监控
startMonitoring(dataSource);
// 等待线程完成任务后关闭
executor.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
// 执行查询操作
private static void executeQuery(String query) {
try (Connection connection = dataSource.getConnection();
PreparedStatement ps = connection.prepareStatement(query)) {
ResultSet rs = ps.executeQuery();
if (rs.next()) {
System.out.println("Result: " + rs.getInt(1));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
// 动态监控连接池参数
private static void startMonitoring(ComboPooledDataSource dataSource) {
Timer timer = new Timer(true);
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
System.out.println("==== C3P0 Monitoring ====");
System.out.println("Num Connections: " + dataSource.getNumConnectionsDefaultUser());
System.out.println("Num Idle Connections: " + dataSource.getNumIdleConnectionsDefaultUser());
System.out.println("Num Busy Connections: " + dataSource.getNumBusyConnectionsDefaultUser());
System.out.println("Total Connections: " + dataSource.getNumConnections());
System.out.println("Busy Connections: " + dataSource.getNumBusyConnections());
System.out.println("Idle Connections: " + dataSource.getNumIdleConnections());
System.out.println("Unclosed Orphaned Connections: " + dataSource.getNumUnclosedOrphanedConnections());
System.out.println("Statements Cached: " + dataSource.getNumStatementsCached());
System.out.println("=========================");
} catch (SQLException e) {
e.printStackTrace();
}
}
}, 0, 5000); // 每 5 秒监控一次
}
}
三. 运行结果
==== C3P0 Monitoring ====
Num Connections: 15
Num Idle Connections: 12
Num Busy Connections: 3
Total Connections: 15
Busy Connections: 3
Idle Connections: 12
Unclosed Orphaned Connections: 0
Statements Cached: 30
=========================
Result: 1200
四. 优化建议
基础负载
- 小型应用:
minPoolSize=5
,maxPoolSize=20
。 - 大型应用:根据并发量和数据库性能动态调整,例如
minPoolSize=20
,maxPoolSize=200
。
动态监控
结合监控日志和运行结果,实时调整配置:
- 如果
Num Idle Connections
长期过多,降低minPoolSize
。 - 如果
Num Busy Connections
经常接近maxPoolSize
,需要增加连接池上限或优化数据库查询。
错误处理
- 配置合理的
checkoutTimeout
,避免死锁。 - 使用
testConnectionOnCheckout
和idleConnectionTestPeriod
检测无效连接。