免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 12535 | 回复: 4
打印 上一主题 下一主题

浅析 jdbc 数据库连接池 druid & proxool [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-11-12 21:21 |只看该作者 |倒序浏览
浅析 jdbc 数据库连接池 druid & proxool




上周我们发生了新版上线以来的最大的一次故障,表现为数据库连接数暴增,所有的应用无法连接数据库,数据库服务器负载居高不下,最终导致数据库服务器宕机。经过了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次取连接和关闭连接耗时。部分代码贴图
   
Java代码
  1. 1.PoolTestInterface testPool = poolTestMap.get(poolKey);   
  2. 2.        Connection conn = testPool.getConnection();   
  3. 3.        try {   
  4. 4.            try {   
  5. 5.                PreparedStatement statement = conn.prepareStatement("select 1");   
  6. 6.                ResultSet rs = statement.executeQuery();   
  7. 7.                rs.close();   
  8. 8.                statement.close();   
  9. 9.            } catch (SQLException e) {   
  10. 10.                e.printStackTrace();   
  11. 11.            }   
  12. 12.        } finally {   
  13. 13.            if (conn != null){   
  14. 14.                try {   
  15. 15.                    conn.close();   
  16. 16.                    conn = null;   
  17. 17.                } catch (SQLException e) {   
  18. 18.                }   
  19. 19.            }   
  20. 20.        }  
  21. PoolTestInterface testPool = poolTestMap.get(poolKey);
  22.                 Connection conn = testPool.getConnection();
  23.                 try {
  24.                         try {
  25.                                 PreparedStatement statement = conn.prepareStatement("select 1");
  26.                                 ResultSet rs = statement.executeQuery();
  27.                                 rs.close();
  28.                                 statement.close();
  29.                         } catch (SQLException e) {
  30.                                 e.printStackTrace();
  31.                         }
  32.                 } finally {
  33.                         if (conn != null){
  34.                                 try {
  35.                                         conn.close();
  36.                                         conn = null;
  37.                                 } catch (SQLException e) {
  38.                                 }
  39.                         }
  40.                 }
复制代码
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全部报无法获取空闲连接,已经达到设置的最大连接数
在线程多,而最大连接数少的情况下,连接都不空闲,无法获得连接。

附:部分测试代码

Java代码
  1. 1.获得不同连接池的接口   
  2. 2.  
  3. 3.public interface PoolTestInterface {   
  4. 4.    /**  
  5. 5.     * 获得数据库连接  
  6. 6.     * @return  
  7. 7.     */  
  8. 8.    public Connection getConnection();   
  9. 9.    /**  
  10. 10.     * 连接池的不同的标示  
  11. 11.     * @return  
  12. 12.     */  
  13. 13.    public String getConnectionPoolKey();   
  14. 14.}  
  15. 获得不同连接池的接口

  16. public interface PoolTestInterface {
  17.         /**
  18.          * 获得数据库连接
  19.          * @return
  20.          */
  21.         public Connection getConnection();
  22.         /**
  23.          * 连接池的不同的标示
  24.          * @return
  25.          */
  26.         public String getConnectionPoolKey();
  27. }
复制代码
Java代码
  1. 1.//druid连接池实例   
  2. 2.public class DruidPoolInstence extends AbstractPoolInstence {   
  3. 3.  
  4. 4.    DruidDataSource dataSource = null;   
  5. 5.  
  6. 6.    @Override  
  7. 7.    public String getConnectionPoolKey() {   
  8. 8.        return "druid";   
  9. 9.    }   
  10. 10.  
  11. 11.    @Override  
  12. 12.    protected DataSource getDataSource() {   
  13. 13.        if (dataSource == null) {   
  14. 14.            dataSource = new DruidDataSource();   
  15. 15.            dataSource.setMaxActive(100);   
  16. 16.            dataSource.setMaxIdle(30);   
  17. 17.            dataSource.setMinIdle(20);   
  18. 18.            dataSource.setInitialSize(10);   
  19. 19.            dataSource.setPoolPreparedStatements(true);   
  20. 20.            dataSource.setTestOnBorrow(false);   
  21. 21.            dataSource.setTestOnReturn(false);   
  22. 22.            dataSource.setMinEvictableIdleTimeMillis(30);   
  23. 23.            dataSource.setMaxWaitThreadCount(1000);   
  24. 24.            dataSource.setDriverClassName("com.mysql.jdbc.Driver");   
  25. 25.            dataSource.setUrl("jdbc:mysql://localhost:3306/lottery");   
  26. 26.            dataSource.setUsername("root");   
  27. 27.            dataSource.setPassword("root");   
  28. 28.        }   
  29. 29.        return dataSource;   
  30. 30.    }   
  31. 31.}  
  32. //druid连接池实例
  33. public class DruidPoolInstence extends AbstractPoolInstence {

  34.         DruidDataSource dataSource = null;

  35.         @Override
  36.         public String getConnectionPoolKey() {
  37.                 return "druid";
  38.         }

  39.         @Override
  40.         protected DataSource getDataSource() {
  41.                 if (dataSource == null) {
  42.                         dataSource = new DruidDataSource();
  43.                         dataSource.setMaxActive(100);
  44.                         dataSource.setMaxIdle(30);
  45.                         dataSource.setMinIdle(20);
  46.                         dataSource.setInitialSize(10);
  47.                         dataSource.setPoolPreparedStatements(true);
  48.                         dataSource.setTestOnBorrow(false);
  49.                         dataSource.setTestOnReturn(false);
  50.                         dataSource.setMinEvictableIdleTimeMillis(30);
  51.                         dataSource.setMaxWaitThreadCount(1000);
  52.                         dataSource.setDriverClassName("com.mysql.jdbc.Driver");
  53.                         dataSource.setUrl("jdbc:mysql://localhost:3306/lottery");
  54.                         dataSource.setUsername("root");
  55.                         dataSource.setPassword("root");
  56.                 }
  57.                 return dataSource;
  58.         }
  59. }
复制代码
Java代码
  1. 1.//proxool连接池实例   
  2. 2.public class ProxoolInstence extends AbstractPoolInstence {   
  3. 3.  
  4. 4.    ProxoolDataSource dataSource = null;   
  5. 5.  
  6. 6.    @Override  
  7. 7.    public String getConnectionPoolKey() {   
  8. 8.        return "proxool";   
  9. 9.    }   
  10. 10.  
  11. 11.    @Override  
  12. 12.    protected DataSource getDataSource() {   
  13. 13.        if (dataSource == null) {   
  14. 14.            dataSource = new ProxoolDataSource();   
  15. 15.            dataSource.setDriver("com.mysql.jdbc.Driver");   
  16. 16.            dataSource.setDriverUrl("jdbc:mysql://localhost:3306/lottery");   
  17. 17.            dataSource.setUser("root");   
  18. 18.            dataSource.setPassword("root");   
  19. 19.  
  20. 20.            dataSource.setAlias("test_proxool");   
  21. 21.            dataSource.setMaximumConnectionCount(100);   
  22. 22.            dataSource.setMinimumConnectionCount(20);   
  23. 23.            dataSource.setMaximumActiveTime(600000);   
  24. 24.            dataSource.setHouseKeepingSleepTime(90000);   
  25. 25.            dataSource.setSimultaneousBuildThrottle(20);   
  26. 26.            dataSource.setPrototypeCount(20);   
  27. 27.            dataSource.setTestBeforeUse(false);   
  28. 28.            dataSource.setHouseKeepingTestSql("select 1 from dual");   
  29. 29.        }   
  30. 30.        return dataSource;   
  31. 31.    }   
  32. 32.}  
  33. //proxool连接池实例
  34. public class ProxoolInstence extends AbstractPoolInstence {

  35.         ProxoolDataSource dataSource = null;

  36.         @Override
  37.         public String getConnectionPoolKey() {
  38.                 return "proxool";
  39.         }

  40.         @Override
  41.         protected DataSource getDataSource() {
  42.                 if (dataSource == null) {
  43.                         dataSource = new ProxoolDataSource();
  44.                         dataSource.setDriver("com.mysql.jdbc.Driver");
  45.                         dataSource.setDriverUrl("jdbc:mysql://localhost:3306/lottery");
  46.                         dataSource.setUser("root");
  47.                         dataSource.setPassword("root");

  48.                         dataSource.setAlias("test_proxool");
  49.                         dataSource.setMaximumConnectionCount(100);
  50.                         dataSource.setMinimumConnectionCount(20);
  51.                         dataSource.setMaximumActiveTime(600000);
  52.                         dataSource.setHouseKeepingSleepTime(90000);
  53.                         dataSource.setSimultaneousBuildThrottle(20);
  54.                         dataSource.setPrototypeCount(20);
  55.                         dataSource.setTestBeforeUse(false);
  56.                         dataSource.setHouseKeepingTestSql("select 1 from dual");
  57.                 }
  58.                 return dataSource;
  59.         }
  60. }
复制代码
下面的代码就是测试代码,写的不规范,见谅

Java代码
  1. 1.public abstract class AbstractConnectionPoolTest {   
  2. 2.    // 消耗时间:MS   
  3. 3.    public static long cusTime = 0;   
  4. 4.    // 线程个数   
  5. 5.    static int threadNum = 10;   
  6. 6.    // 循环次数   
  7. 7.    static int forCount = 10;   
  8. 8.    // 总共执行次数   
  9. 9.    static int excuteCount = 0;   
  10. 10.      
  11. 11.    static ExecutorService executorService = Executors.newFixedThreadPool(threadNum);   
  12. 12.    public static Map<String, PoolTestInterface> poolTestMap = new HashMap<String, PoolTestInterface>();   
  13. 13.    public static final String DRUID = "druid";   
  14. 14.    public static final String PROXOOL = "proxool";   
  15. 15.    static {   
  16. 16.        poolTestMap.put("druid", new DruidPoolInstence());   
  17. 17.        poolTestMap.put("proxool", new ProxoolInstence());   
  18. 18.    }   
  19. 19.      
  20. 20.    public synchronized static void addCusTime(long time) {   
  21. 21.        cusTime += time;   
  22. 22.        excuteCount++;   
  23. 23.        System.out.println("单次执行完成耗时:" + time);   
  24. 24.        System.out.println("执行完成共耗时:" + cusTime);   
  25. 25.        if (excuteCount == threadNum * forCount) {   
  26. 26.            System.out.println("全部执行完成执行平均耗时:" + cusTime / excuteCount);   
  27. 27.            executorService.shutdown();   
  28. 28.        }   
  29. 29.    }   
  30. 30.}  
  31. public abstract class AbstractConnectionPoolTest {
  32.         // 消耗时间:MS
  33.         public static long cusTime = 0;
  34.         // 线程个数
  35.         static int threadNum = 10;
  36.         // 循环次数
  37.         static int forCount = 10;
  38.         // 总共执行次数
  39.         static int excuteCount = 0;
  40.        
  41.         static ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
  42.         public static Map<String, PoolTestInterface> poolTestMap = new HashMap<String, PoolTestInterface>();
  43.         public static final String DRUID = "druid";
  44.         public static final String PROXOOL = "proxool";
  45.         static {
  46.                 poolTestMap.put("druid", new DruidPoolInstence());
  47.                 poolTestMap.put("proxool", new ProxoolInstence());
  48.         }
  49.        
  50.         public synchronized static void addCusTime(long time) {
  51.                 cusTime += time;
  52.                 excuteCount++;
  53.                 System.out.println("单次执行完成耗时:" + time);
  54.                 System.out.println("执行完成共耗时:" + cusTime);
  55.                 if (excuteCount == threadNum * forCount) {
  56.                         System.out.println("全部执行完成执行平均耗时:" + cusTime / excuteCount);
  57.                         executorService.shutdown();
  58.                 }
  59.         }
  60. }
复制代码
Java代码
  1. 1.public class OpenConnectionTest extends AbstractConnectionPoolTest {   
  2. 2.    public static void openConnTest(String poolKey) {   
  3. 3.        PoolTestInterface testPool = poolTestMap.get(poolKey);   
  4. 4.        Connection conn = testPool.getConnection();   
  5. 5.        try {   
  6. 6.            try {   
  7. 7.                PreparedStatement statement = conn.prepareStatement("select 1");   
  8. 8.                ResultSet rs = statement.executeQuery();   
  9. 9.                rs.close();   
  10. 10.                statement.close();   
  11. 11.            } catch (SQLException e) {   
  12. 12.                e.printStackTrace();   
  13. 13.            }   
  14. 14.        } finally {   
  15. 15.            if (conn != null)   
  16. 16.                try {   
  17. 17.                    conn.close();   
  18. 18.                    conn = null;   
  19. 19.                } catch (SQLException e) {   
  20. 20.                }   
  21. 21.        }   
  22. 22.    }   
  23. 23.  
  24. 24.    public static void main(String[] args) {   
  25. 25.        Map<String, Long> timeMap = new HashMap<String, Long>();   
  26. 26.        String poolKeyArr[] = { PROXOOL, DRUID };   
  27. 27.        for (int j = 0; j < forCount; j++) {   
  28. 28.            for (String poolKey : poolKeyArr) {   
  29. 29.                try {   
  30. 30.                    long start = System.currentTimeMillis();   
  31. 31.                    System.out.println(poolKey + "单线程1000次开始执行");   
  32. 32.                    for (int i = 0; i < 1000; i++) {   
  33. 33.                        openConnTest(poolKey);   
  34. 34.                    }   
  35. 35.                    long end = System.currentTimeMillis();   
  36. 36.  
  37. 37.                    long t = end - start;   
  38. 38.                    Long tempT = timeMap.get(poolKey) == null ? 0l : timeMap   
  39. 39.                            .get(poolKey);   
  40. 40.                    timeMap.put(poolKey, tempT + t);   
  41. 41.                    System.out   
  42. 42.                            .println(poolKey + "单线程1000次执行结束" + (end - start));   
  43. 43.                } catch (Exception e) {   
  44. 44.                    e.printStackTrace();   
  45. 45.                    continue;   
  46. 46.                }   
  47. 47.            }   
  48. 48.        }   
  49. 49.  
  50. 50.        for (String pk : poolKeyArr) {   
  51. 51.            System.out.println(pk + "执行1000次单线程开关连接任务平均耗时为:" + timeMap.get(pk)   
  52. 52.                    / forCount);   
  53. 53.        }   
  54. 54.    }   
  55. 55.}  
复制代码

论坛徽章:
0
2 [报告]
发表于 2011-11-14 09:54 |只看该作者
ll辛苦了...谢谢分享....

论坛徽章:
0
3 [报告]
发表于 2016-03-29 17:07 |只看该作者
赞 真的赞 非常赞 重要的事情说三遍

论坛徽章:
130
亥猪
日期:2016-05-06 16:12:48水瓶座
日期:2016-05-06 16:13:28白羊座
日期:2016-05-06 16:14:17射手座
日期:2016-05-06 16:14:17巳蛇
日期:2016-05-06 16:13:28午马
日期:2016-05-06 16:12:48辰龙
日期:2016-05-06 16:13:28天蝎座
日期:2016-05-06 16:13:28丑牛
日期:2016-05-06 16:13:28白羊座
日期:2016-05-06 16:13:28水瓶座
日期:2016-05-06 16:13:28未羊
日期:2016-05-06 16:12:48
4 [报告]
发表于 2016-03-29 17:37 |只看该作者
回复 3# qq31715879


   

论坛徽章:
0
5 [报告]
发表于 2016-03-31 10:13 |只看该作者
学习了!回复 1# 听老歌


   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP