简介

在上周阅读Alibaba Druid数据库连接池后,感觉光看有点领会不到精髓,后面这几篇文章将尝试自己实现一个数据库连接池Demo

原生JDBC与Alibaba Druid使用

我们先把相关的测试给搭建起来,把JDBC、Druid的相关示例代码跑起来,看看效果和性能

在实现一个最简单的自定义连接池,然后运行三者进行对比

原生JDBC

我们先使用原生JDBC将数据库数据初始化,然后去查询,代码如下:

public class Main {static final String DB_URL = "jdbc:h2:file:./demo-db";static final String USER = "sa";static final String PASS = "sa";/*** 生成数据*/private static void initData() {final String drop = "drop table `user_example` if exists;";final String createTable = "CREATE TABLE IF NOT EXISTS `user_example` (" +"`id` bigint NOT NULL AUTO_INCREMENT, " +"`name` varchar(100) NOT NULL" +");";final String addUser = "insert into user_example (name) values(%s)";try(Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);Statement stmt = conn.createStatement()) {stmt.execute(drop);stmt.execute(createTable);for (int i=0; i<10; i++) {stmt.execute(String.format(addUser, i));}conn.commit();} catch (SQLException e) {e.printStackTrace();}}
}

然后原始暴力查询:

public class Main {/*** 原生JDBC查询*/private static void rawExample() {for (int i=0; i<queryAmount; i++) {// Open a connectiontry(Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);Statement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery(QUERY);) {// Extract data from result setwhile (rs.next()) {// Retrieve by column nameSystem.out.print("ID: " + rs.getInt("id"));System.out.print(", name: " + rs.getString("name"));System.out.print(";");}System.out.println();} catch (SQLException e) {e.printStackTrace();}}}
}

Alibaba Druid查询

使用Alibaba Druid进行相同的查询操作:

public class Main {/*** Alibaba Druid查询*/private static void druidExample() throws Exception {DruidDataSource dataSource = new DruidDataSource();dataSource.setInitialSize(1);dataSource.setMaxActive(1);dataSource.setMinIdle(1);dataSource.setDriverClassName("org.h2.Driver");dataSource.setUrl(DB_URL);dataSource.setUsername(USER);dataSource.setPassword(PASS);for (int i=0; i<queryAmount; i++) {// Open a connectiontry(Connection conn = dataSource.getConnection();Statement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery(QUERY)) {// Extract data from result setwhile (rs.next()) {// Retrieve by column nameSystem.out.print("ID: " + rs.getInt("id"));System.out.print(", name: " + rs.getString("name"));System.out.print(";");}System.out.println();} catch (SQLException e) {e.printStackTrace();}}}
}

自写最简单版本查询

相关的思路如下:

  • 1.基于接口Database写一个自己的Database:SelfDatasource

    • 实现自己的getConnection方法
  • 2.基于接口:javax.sql.PooledConnection, Connection,自定义数据物理连接
    • 为了自定义close方法,将数据库连接回收复用

自定义Database

列出来的就是要进行自己实现,其他方法暂时默认:

在测试代码中,就涉及到一个getConnection函数,以及自定义的连接池复用相关

public class SelfDataSource implements DataSource {/*** 放置空闲可用的连接*/private final Queue<SelfPoolConnection> idle = new LinkedList<>();/*** 放置正在使用的连接*/private final Set<SelfPoolConnection> running = new HashSet<>();private final String url;private final String username;private final String password;public SelfDataSource(final String url, final String username, final String password) {this.url = url;this.username = username;this.password = password;}/*** 初步将Connection从运行池中异常,放入空闲池* 从正在使用连接池中移除,放入空闲连接池中* @param selfPoolConnection 自定义Connection*/public void recycle(final SelfPoolConnection selfPoolConnection) {running.remove(selfPoolConnection);idle.add(selfPoolConnection);System.out.println("回收连接");}/*** 自定义的获取数据库物理连接* 1.无空闲连接则生成新的物理连接,并且放入正在使用连接池中* 2.如果有空闲连接,则获取,并放入正在使用连接池中* @return 自定义的数据库物理连接(自定义以能够自定义Close方法)* @throws SQLException*/@Overridesynchronized public Connection getConnection() throws SQLException {if (idle.isEmpty()) {System.out.println("生成新物理连接");SelfPoolConnection conn = new SelfPoolConnection(this, url, username, password);running.add(conn);return conn.getConnection();}SelfPoolConnection conn = idle.poll();running.add(conn);return conn.getConnection();}
}

自定义Connection

因为在测试代码中,涉及的需要实现的函数如下:

  • createStatement
  • close(try自动调用)
  • getConnection(自定义DataSource调用)
public class SelfPoolConnection implements javax.sql.PooledConnection, Connection {private final SelfDataSource selfDatasource;private Connection connection;public SelfPoolConnection(final SelfDataSource selfDatasource, final String url, final String username, final String password) {this.selfDatasource = selfDatasource;System.out.println("初始化物理连接");try {connection = DriverManager.getConnection(url, username, password);} catch (SQLException e) {e.printStackTrace();}}@Overridepublic Statement createStatement() throws SQLException {return connection.createStatement();}@Overridesynchronized public Connection getConnection() throws SQLException {return this;}/*** 关闭连接,调用自定义DataSource用于复用* 目前感觉这样不规范,但时间紧张,前期先简单实现* @throws SQLException*/@Overridepublic void close() throws SQLException {selfDatasource.recycle(this);}
}

运行对比

运行函数与结果如下:

public class Main {public static void main(String[] args) throws Exception {initData();long current = System.currentTimeMillis();rawExample();System.out.printf("原生查询耗时:%d 毫秒\n", System.currentTimeMillis() - current);current = System.currentTimeMillis();druidExample();System.out.printf("连接池查询耗时:%d 毫秒\n", System.currentTimeMillis() - current);current = System.currentTimeMillis();selfExample();System.out.printf("自写连接池查询耗时:%d 毫秒\n", System.currentTimeMillis() - current);Thread.sleep(3000);}
}

结果:

ID: 1, name: 0;ID: 2, name: 1;ID: 3, name: 2;ID: 4, name: 3;ID: 5, name: 4;ID: 6, name: 5;ID: 7, name: 6;ID: 8, name: 7;ID: 9, name: 8;ID: 10, name: 9;
ID: 1, name: 0;ID: 2, name: 1;ID: 3, name: 2;ID: 4, name: 3;ID: 5, name: 4;ID: 6, name: 5;ID: 7, name: 6;ID: 8, name: 7;ID: 9, name: 8;ID: 10, name: 9;
ID: 1, name: 0;ID: 2, name: 1;ID: 3, name: 2;ID: 4, name: 3;ID: 5, name: 4;ID: 6, name: 5;ID: 7, name: 6;ID: 8, name: 7;ID: 9, name: 8;ID: 10, name: 9;
ID: 1, name: 0;ID: 2, name: 1;ID: 3, name: 2;ID: 4, name: 3;ID: 5, name: 4;ID: 6, name: 5;ID: 7, name: 6;ID: 8, name: 7;ID: 9, name: 8;ID: 10, name: 9;
ID: 1, name: 0;ID: 2, name: 1;ID: 3, name: 2;ID: 4, name: 3;ID: 5, name: 4;ID: 6, name: 5;ID: 7, name: 6;ID: 8, name: 7;ID: 9, name: 8;ID: 10, name: 9;
原生查询耗时:220 毫秒
11月 15, 2021 10:09:04 下午 com.alibaba.druid.pool.DruidDataSource error
严重: testWhileIdle is true, validationQuery not set
11月 15, 2021 10:09:04 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
ID: 1, name: 0;ID: 2, name: 1;ID: 3, name: 2;ID: 4, name: 3;ID: 5, name: 4;ID: 6, name: 5;ID: 7, name: 6;ID: 8, name: 7;ID: 9, name: 8;ID: 10, name: 9;
ID: 1, name: 0;ID: 2, name: 1;ID: 3, name: 2;ID: 4, name: 3;ID: 5, name: 4;ID: 6, name: 5;ID: 7, name: 6;ID: 8, name: 7;ID: 9, name: 8;ID: 10, name: 9;
ID: 1, name: 0;ID: 2, name: 1;ID: 3, name: 2;ID: 4, name: 3;ID: 5, name: 4;ID: 6, name: 5;ID: 7, name: 6;ID: 8, name: 7;ID: 9, name: 8;ID: 10, name: 9;
ID: 1, name: 0;ID: 2, name: 1;ID: 3, name: 2;ID: 4, name: 3;ID: 5, name: 4;ID: 6, name: 5;ID: 7, name: 6;ID: 8, name: 7;ID: 9, name: 8;ID: 10, name: 9;
ID: 1, name: 0;ID: 2, name: 1;ID: 3, name: 2;ID: 4, name: 3;ID: 5, name: 4;ID: 6, name: 5;ID: 7, name: 6;ID: 8, name: 7;ID: 9, name: 8;ID: 10, name: 9;
连接池查询耗时:69 毫秒
生成新物理连接
初始化物理连接
ID: 1, name: 0;ID: 2, name: 1;ID: 3, name: 2;ID: 4, name: 3;ID: 5, name: 4;ID: 6, name: 5;ID: 7, name: 6;ID: 8, name: 7;ID: 9, name: 8;ID: 10, name: 9;
回收连接
ID: 1, name: 0;ID: 2, name: 1;ID: 3, name: 2;ID: 4, name: 3;ID: 5, name: 4;ID: 6, name: 5;ID: 7, name: 6;ID: 8, name: 7;ID: 9, name: 8;ID: 10, name: 9;
回收连接
ID: 1, name: 0;ID: 2, name: 1;ID: 3, name: 2;ID: 4, name: 3;ID: 5, name: 4;ID: 6, name: 5;ID: 7, name: 6;ID: 8, name: 7;ID: 9, name: 8;ID: 10, name: 9;
回收连接
ID: 1, name: 0;ID: 2, name: 1;ID: 3, name: 2;ID: 4, name: 3;ID: 5, name: 4;ID: 6, name: 5;ID: 7, name: 6;ID: 8, name: 7;ID: 9, name: 8;ID: 10, name: 9;
回收连接
ID: 1, name: 0;ID: 2, name: 1;ID: 3, name: 2;ID: 4, name: 3;ID: 5, name: 4;ID: 6, name: 5;ID: 7, name: 6;ID: 8, name: 7;ID: 9, name: 8;ID: 10, name: 9;
回收连接
自写连接池查询耗时:6 毫秒
Disconnected from the target VM, address: '127.0.0.1:54211', transport: 'socket'

总结

从接口上看,一部分是符合我们预期的:连接池的性能是远远优于不使用连接池的

但自定义的连接池竟然比Druid还快,是我没有想到的,一度有些怀疑

但相关的地址确实是实现了的,从日志上来看,确实是只初始化了一次,后面没有再初始物理连接

目前的例子是单线程,没有考虑加锁、检查、异常处理等,可能是这些有影响,后面我们再研究研究

代码参考地址:https://github.com/lw1243925457/DataSourcePoolDemo

参考链接

  • Alibaba Druid 源码阅读(一) 数据库连接池初步
  • Alibaba Druid 源码阅读(二) 数据库连接池实现初步探索
  • Alibaba Druid 源码阅读(三) 数据库连接池初始化探索
  • Alibaba Druid 源码阅读(四) 数据库连接池中连接获取探索
  • Alibaba Druid 源码阅读(五)数据库连接池 连接关闭探索

数据库连接池Demo(1)单线程初步相关推荐

  1. mysql连接池泄露_一次线上故障:数据库连接池泄露后的思考

    作者:陈朗,普兰金融科技能效工程部开发工程师 一:初步排查 早上作为能效平台系统的使用高峰期,系统负载通常比其它时间段更大一些,某个时间段会有大量用户登录.当天系统开始有用户报障,发布系统线上无法构建 ...

  2. Alibaba Druid 源码阅读(二) 数据库连接池实现初步探索

    Alibaba Druid 源码阅读(二) 数据库连接池实现初步探索 简介 在上篇文章中,了解了连接池的应用场景和本地运行了示例,本篇文章中,我们尝试来探索下Alibaba Druid数据库连接池的整 ...

  3. Alibaba Druid 源码阅读(一) 数据库连接池初步

    Alibaba Druid 源码阅读(一) 数据库连接池初步 简介 本文将初步探索数据库连接池的应用场景,为后面的源码分析做些准备 数据库连接池的应用场景 在没有连接池之前,在使用中,需要访问数据库时 ...

  4. c#打开数据库连接池的工作机制_数据库连接池-tomcat-jdbc使用笔记

    现在 主流的数据库连接池有:Proxool.C3P0.DBCP.tomcat-jdbc.Druid.其中tomcat-jdbc是tomcat服务器比较可靠的 数据库连接池. Tomcat 在 7.0 ...

  5. swoole实现数据库连接池

    2019独角兽企业重金招聘Python工程师标准>>> 原生 PHP CURD 让我们来回顾一下PHP中数据库的使用 <?php # curd.php$id = 1;$dbh ...

  6. 数据库连接池为什么要用threadlocal呢?不用会怎样?

    点击关注公众号,Java干货及时送达 来源:blog.csdn.net/qq_42405666/article/details/108258820 这个问题我疑问了很久很久,主要如下截图. 个连接对应 ...

  7. c3p0数据库连接池的使用详解

    首先,什么是c3p0?下面是百度百科的解释: C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展.目前使用它的开源项目有Hibernate,Sp ...

  8. JavaWeb基础—数据库连接池DBCP、C3P0

    一.基本概念 数据库连接池负责分配.管理和释放数据库连接 数据库连接池:(池用map来实现居多) 用处:为了可重用(销毁创建麻烦,开销大)(招培训老师的例子) 二.编写实现数据库连接池 池参数: 初识 ...

  9. java 连接池_初探数据库连接池

    参考资料 数据库连接池学习笔记(一):原理介绍+常用连接池介绍 java数据库连接池实现原理 高性能数据库连接池的内幕 1. 为什么要使用连接池 数据库连接是一种关键的有限的昂贵的资源,这一点在多用户 ...

最新文章

  1. 特斯拉“纯视觉路线”能去掉ISP吗?
  2. Linux下getsockopt/setsockopt 函数说明
  3. linux 网卡丢弃多播包,rp_filter及Linux下多网卡接收多播的问题
  4. 蓝桥杯练习系统习题-算法训练5
  5. php之二叉树,PHP构造二叉树算法示例
  6. android 弹出PopupWindow后背景逐渐变暗
  7. NOI数学:大步小步(Baby Step Giant Step,BSGS)算法
  8. 信息论与编码_庆祝中山大学计算机科学系成立40周年系列活动 | 第四届“信息论与编码中大论坛”...
  9. Jmeter并发压测
  10. Java获取世界各国各城市代码_获取世界各国、全国省份、城市、县
  11. c语言中debug的作用,c语言debug怎么用
  12. 使用Nexus在Windows中搭建Maven私服
  13. mobi电子书如何用安卓手机打开?
  14. ACdream 1121 喵喵的IDE
  15. instead of触发器
  16. 【TAPD】快速上手
  17. 新生儿的一类(免费)疫苗(截止2019年)
  18. PHP excel导出(自定义样式,行高,合并单元格等)
  19. 靠写iPhone程序发财的三个故事
  20. 最新数据挖掘赛事方案梳理!

热门文章

  1. 关于javascript dom扩展:Selector API
  2. POJ 3468 线段树+lazy标记
  3. 2014,都要好好的~
  4. 在DBGrid中实现Copy、Paste功能 - DELPHI
  5. 字体大小自适应屏幕分辨率 CSS解决方案
  6. IIS7下 【请求被中止: 未能创建 SSL/TLS 安全通道 】 解决方法
  7. php安装redis扩展‘checking for igbinary includes... configure: error: Cannot find igbinary.h‘解决方法
  8. 解决“(1146, “Table ‘mydb.django_session‘ doesn‘t exist“)”报错的方法
  9. webpack1.x环境配置与打包基础【附带各种 “坑“ 与解决方案!持续更新中...】
  10. 解决 OpenCV with CUDA 编译提示缺少 nvcuvid.h 的问题