目录
引言
对于有几年后端开发经验的程序员而言,或多或少都有使用过数据库连接池,jdbc也是老生常谈,但是很多人或许仅是停留在会使用,至于底层是怎么实现的、为何要使用它、不使用它是否可以、什么场景下使用它等也不会去深入了解,反正是在搭好的框架上开发,别人这么配,我也跟着配,配置好数据库连接,专注于业务逻辑开发即可,仅止步于数据库的增删改查。
一、数据库连接池简介
1、what? 什么池?
简单来说,就是提前创建保存大量的资源(对象),以备不时之需。
常见池化技术:
-
线程池:管理线程对象的池
-
对象池:初始化一些对象的池,如jdk的静态常量池、运行时常量池等
-
连接池:管理连接对象的池
常见:
-
1、C3PO-开源连接池,Hibernate,Spring
-
2、BonecP--apache出品tomcat使用
-
3、Druid--阿里出品,史上最强没有之一,对所有sql的监控
-
4、HikaricP--springboot2,java最快连接池
2、where? 在哪里应用?
资源的合理管理与分配,多个应用连接同一个数据库时,更不能因为某个应用独占所有的资源连接,导致其他应用无法提供服务。
什么东西需要池化技术?
-
有限资源-数据库连接,计划分配,资源管控,减少开销
-
开销大/耗时-线程池,开辟栈空间,创建/销毁开销大
场景(运营)需求:
-
1、管理MySQL连接
-
2、200并发量
-
3、MySQL Pool最大连接数为10---限流
-
4、服务对象维护
3、数据库连接池的实现及原理
注:一些原理及概念的文字,不想看的直接跳过到实战应用,摘自
https://blog.csdn.net/wangjunjie0817/article/details/89380841
对于一个简单的数据库应用,由于对于数据库的访问不是很频繁。这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什么明显的性能上的开销。但是对于一个复杂的数据库应用,情况就完全不同了。频繁的建立、关闭连接,会极大的减低系统的性能,因为对于连接的使用成了系统性能的瓶颈。
连接复用:
通过建立一个数据库连接池以及一套连接使用管理策略,使得一个数据库连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。
资源池:
对于共享资源,有一个很著名的设计模式:资源池。
该模式正是为了解决资源频繁分配、释放所造成的问题的。把该模式应用到数据库连接管理领域,就是建立一个数据库连接池,提供一套高效的连接分配、使用策略,最终目标是实现连接的高效、安全的复用。
基本原理:
在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法。如:外部使用者可通过getConnection方法。
方法获取连接,使用完毕后再通过releaseConnection 方法将连接返回,注意此时连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。
4、数据库连接池的优点
-
资源重用
-
更快的系统响应速度
-
新的资源分配手段
-
统一的连接管理,避免数据库连接泄漏
5、连接池与jdbc的关系
JDBC连接池 在标准JDBC对应用的接口中,并没有提供资源的管理方法。所以,缺省的资源管理由应用自己负责。虽然在JDBC规范中,多次提及资源的关闭/回收及其他的合理运用。但最稳妥的方式,还是为应用提供有效的管理手段。所以,JDBC为第三方应用服务器(Application Server)提供了一个由数据库厂家实现的管理标准接口:连接缓冲(connection pooling)。引入了连接池( Connection Pool )的概念 ,也就是以缓冲池的机制管理数据库的资源。
JDBC最常用的资源有三类:
-
Connection: 数据库连接
-
Statement: 会话声明
-
ResultSet: 结果集游标
这是一种“爷—父—子”的关系,对Connection的管理,就是对数据库资源的管理。
举个例子: 如果想确定某个数据库连接(Connection)是否超时,则需要确定其(所有的)子Statement是否超时,同样,需要确定所有相关的 ResultSet是否超时;在关闭Connection前,需要关闭所有相关的Statement和ResultSet。因此,连接池(Connection Pool)所起到的作用,不仅仅简单地管理Connection,还涉及到 Statement和ResultSet。
6、基本功能
-
初始化:预先创建好对象,放入池中
-
设定最大数,防止无尽连接,资源耗尽
-
客户端从池中出借空闲连接对象
-
客户端归还连接对象回池
-
体检-自我检测
7、设计思路
(1)获取连接 getConnect()
-
从idle中取出对象,放入busy
-
Idle有值,直接取
-
ldle无值,若池子未满,新建
-
池满全繁忙,等待返还
(2)释放连接 releaseConnect()
-
从繁忙工作队列中移除对象,放入空闲队列中
二、应用实战
1、SpringBoot应用
新建一个SpringBoot应用:mini-pool
2、依赖
maven 主要依赖,jdbc连接mysql的驱动
3、jdbc工具类
新建一个创建jdbc连接的工具类
注:实际项目中,配置信息要填写在配置文件中,这里为了方便演示,就直接写死再代码中了。
4、连接池接口定义
新增一个 MiniPool 简单的连接池接口,包含如下方法:初始化、销毁连接、获取连接、释放连接等
5、具体实现
MiniPoolImpl 实现 MiniPool接口方法
@Slf4j
public class MiniPoolImpl implements MiniPool {
// 空闲连接队列
private LinkedBlockingQueue<Connection> idleQueue;
// 工作中队列
private LinkedBlockingQueue<Connection> busyQueue;
// 连接池最大数量
private final int maxSize;
// 存活的连接数,一开始默认0
private AtomicInteger activeSize = new AtomicInteger(0);
public MiniPoolImpl(int maxSize){
this.maxSize = maxSize;
}
/**
* 初始化连接池
*/
@Override
public void init() {
idleQueue = new LinkedBlockingQueue<Connection>();
busyQueue = new LinkedBlockingQueue<Connection>();
}
/**
* 销毁
*/
@Override
public void destory() {
}
...
}
其中,释放连接方法:
主要是获取连接方法:
-
(1)判断空闲队列是否有值,有直接取返回
-
(2)若空闲队列无值,而且连接池未满,则新建一个连接
-
(3)连接池全繁忙工作中,阻塞等待一定时间返回
其中,第2步同步代码块部分,可使用原子类 AtomicInteger 优化
// 存活的连接数,一开始默认0
private AtomicInteger activeSize = new AtomicInteger(0);
注意:
-
activeSize.get() 获取当前活跃连接数
-
activeSize.incrementAndGet() 先加1再获取(原子操作)
这里,因为 activeSize.incrementAndGet() 比 activeSize.get() 性能高,所以双重判断比只用 activeSize.incrementAndGet() 判断好,锁的双重判断。
配置注册到Spring容器管理:
6、Controller
编写测试类Controller,新增一个http接口方法,便于发送http请求
7、业务逻辑类
具体的业务逻辑测试类,因为要模拟200并发访问,使用多线程技术模拟,业务处理只是简单地往数据库插入200条数据,主要是观察日志输出,是否有需要排队等待获取连接的情况,最好是打断点观察。
注:这里为了简单测试,就不写接口了。
8、测试
(1)t_user表数据清空
(2)启动应用,浏览器访问 localhost:8080/test?times=10 地址20次(快速点击20次),输出结果:
从日志中,可以看到,在数据库连接池最大数量配置为10个,200并发访问时,有部分需要排队等待获取连接。
(3)查看数据库,已经成功插入200条数据
参考资料:
享学课堂公开课
https://blog.csdn.net/wangjunjie0817/article/details/89380841
往期推荐
●日均5亿查询量的京东到家订单中心,为什么舍MySQL用ES?
●四张图带你了解Tomcat系统架构--让面试官颤抖的Tomcat回答系列!
●阿里巴巴为什么能抗住90秒100亿?--服务端高并发分布式架构演进之路
●SpringCloud电商秒杀微服务-Redisson分布式锁方案
一只 有深度 有灵魂 的公众号0.0