很久没上来写点东西,正好这几天在测试一个数据库连接池。顺便上来MARK一下,露个脸啥的.......
上周我们发生了新版上线以来的最大的一次故障,表现为数据库连接数暴增,所有的应用无法连接数据库,数据库服务器负载居高不下,最终导致数据库服务器宕机。经过了6个多小时的处理才慢慢的恢复过来。在这个故障中,现象是数据库服务器宕机了,但是真正导致此事故的原因并不是由于数据库本身造成的。而数据库服务器的宕机,也就让恢复的时间变的相对较长。于是领导要求无论在什么故障下,都要能让数据库正常提供服务,至少要保证能登陆进去进行一些操作。再根据最近的优化需求,应用层打算先从数据库连接池入手。
我们现在使用的数据库连接池为proxool,这个连接池的性能和稳定性都是不错的,网上已经有很多关于此连接池的介绍,proxool广泛用于大型的WEB应用中,口碑不错。druid是阿里巴巴开源的一个项目,由温少等开发(什么?不认识?我也没见过!),wiki:http://code.alibabatech.com/wiki/display/Druid/Home
在这个WIKI中,文档中详细的介绍了关于druid dataSource的用法和性能测试。但是文档中没有包含与proxool中的测试。
今天我们要做的事情是基于proxool+mysql以及druid+mysql的数据库连接池性能,稳定性,监控方面的比较与测试,由于是第一次接触此方面的东西进行测试,希望各位能指出不对的地方,感激不尽。借用网友的话:欢迎拍砖!具体的测试参数和代码将在文后给出。
一,性能测试
1、循环100次单线程1000次取连接和关闭连接耗时。部分代码贴图
PoolTestInterface testPool = poolTestMap.get(poolKey);
Connection conn = testPool.getConnection();
try {
try {
PreparedStatement statement = conn.prepareStatement("select 1");
ResultSet rs = statement.executeQuery();
rs.close();
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
} finally {
if (conn != null){
try {
conn.close();
conn = null;
} catch (SQLException e) {
}
}
}
proxool执行1000次单线程开关连接任务平均耗时为:220 MS
druid执行1000次单线程开关连接任务平均耗时为:72 MS
测试数据要重点说明一下为什么要用平均值,两个连接池在初始化的时候消耗的时间都比较多,而且可以肯定的是连接池中初始化连接数越多,所消耗的时间也越多。(这就是为什么我们的应用的设置初始化数据库连接的时候要根据实际状况设置,设置的初始化连接数越多,启动就会越慢,但是启动之后第一批用户访问的速度会快一点。),所以用了一个100次的循环来取平均耗时,使得测试数据更具有参考性。
2、10线程1000次获得连接并执行一次简单SQL查询的平均耗时对比
proxool全部执行完成平均耗时:13MS
druid全部执行完成平均耗时:10MS
3、10线程10000次获得连接并执行一次简单SQL查询的平均耗时对比
proxool全部执行完成平均耗时:3MS
druid全部执行完成平均耗时:2MS
4、50线程获得连接并执行一次简单SQL查询的耗时对比(连接池最大连接数10个)
druid和proxool全部报无法获取空闲连接,已经达到设置的最大连接数
在线程多,而最大连接数少的情况下,连接都不空闲,无法获得连接。
附:部分测试代码
获得不同连接池的接口
public interface PoolTestInterface {
/**
* 获得数据库连接
* @return
*/
public Connection getConnection();
/**
* 连接池的不同的标示
* @return
*/
public String getConnectionPoolKey();
}
//druid连接池实例
public class DruidPoolInstence extends AbstractPoolInstence {
DruidDataSource dataSource = null;
@Override
public String getConnectionPoolKey() {
return "druid";
}
@Override
protected DataSource getDataSource() {
if (dataSource == null) {
dataSource = new DruidDataSource();
dataSource.setMaxActive(100);
dataSource.setMaxIdle(30);
dataSource.setMinIdle(20);
dataSource.setInitialSize(10);
dataSource.setPoolPreparedStatements(true);
dataSource.setTestOnBorrow(false);
dataSource.setTestOnReturn(false);
dataSource.setMinEvictableIdleTimeMillis(30);
dataSource.setMaxWaitThreadCount(1000);
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/lottery");
dataSource.setUsername("root");
dataSource.setPassword("root");
}
return dataSource;
}
}
//proxool连接池实例
public class ProxoolInstence extends AbstractPoolInstence {
ProxoolDataSource dataSource = null;
@Override
public String getConnectionPoolKey() {
return "proxool";
}
@Override
protected DataSource getDataSource() {
if (dataSource == null) {
dataSource = new ProxoolDataSource();
dataSource.setDriver("com.mysql.jdbc.Driver");
dataSource.setDriverUrl("jdbc:mysql://localhost:3306/lottery");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setAlias("test_proxool");
dataSource.setMaximumConnectionCount(100);
dataSource.setMinimumConnectionCount(20);
dataSource.setMaximumActiveTime(600000);
dataSource.setHouseKeepingSleepTime(90000);
dataSource.setSimultaneousBuildThrottle(20);
dataSource.setPrototypeCount(20);
dataSource.setTestBeforeUse(false);
dataSource.setHouseKeepingTestSql("select 1 from dual");
}
return dataSource;
}
}
下面的代码就是测试代码,写的不规范,见谅
public abstract class AbstractConnectionPoolTest {
// 消耗时间:MS
public static long cusTime = 0;
// 线程个数
static int threadNum = 10;
// 循环次数
static int forCount = 10;
// 总共执行次数
static int excuteCount = 0;
static ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
public static Map<String, PoolTestInterface> poolTestMap = new HashMap<String, PoolTestInterface>();
public static final String DRUID = "druid";
public static final String PROXOOL = "proxool";
static {
poolTestMap.put("druid", new DruidPoolInstence());
poolTestMap.put("proxool", new ProxoolInstence());
}
public synchronized static void addCusTime(long time) {
cusTime += time;
excuteCount++;
System.out.println("单次执行完成耗时:" + time);
System.out.println("执行完成共耗时:" + cusTime);
if (excuteCount == threadNum * forCount) {
System.out.println("全部执行完成执行平均耗时:" + cusTime / excuteCount);
executorService.shutdown();
}
}
}
public class OpenConnectionTest extends AbstractConnectionPoolTest {
public static void openConnTest(String poolKey) {
PoolTestInterface testPool = poolTestMap.get(poolKey);
Connection conn = testPool.getConnection();
try {
try {
PreparedStatement statement = conn.prepareStatement("select 1");
ResultSet rs = statement.executeQuery();
rs.close();
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
} finally {
if (conn != null)
try {
conn.close();
conn = null;
} catch (SQLException e) {
}
}
}
public static void main(String[] args) {
Map<String, Long> timeMap = new HashMap<String, Long>();
String poolKeyArr[] = { PROXOOL, DRUID };
for (int j = 0; j < forCount; j++) {
for (String poolKey : poolKeyArr) {
try {
long start = System.currentTimeMillis();
System.out.println(poolKey + "单线程1000次开始执行");
for (int i = 0; i < 1000; i++) {
openConnTest(poolKey);
}
long end = System.currentTimeMillis();
long t = end - start;
Long tempT = timeMap.get(poolKey) == null ? 0l : timeMap
.get(poolKey);
timeMap.put(poolKey, tempT + t);
System.out
.println(poolKey + "单线程1000次执行结束" + (end - start));
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
for (String pk : poolKeyArr) {
System.out.println(pk + "执行1000次单线程开关连接任务平均耗时为:" + timeMap.get(pk)
/ forCount);
}
}
}
二,监控对比
稍后呈现........