通透理解连接池

一、连接池简介

1.1、连接

客户端想要访问服务器首先需要和服务器创建一个连接,比如数据库连接、redis连接等。而连接相当于是一个资源,服务器资源有限,所以同时可以提供的连接数是有限制的。如果并发的客户端数量较大,就会导致同时需要大量的连接,很可能会导致服务器性能急剧下降,甚至有宕机的风险。对于服务器而言通常会设置最大可以支持的连接数,而对于客户端而言也需要采用珍惜连接资源的措施,采用连接池使连接复用就是很好的一种方式。

1.2、连接池

以数据库连接池为例,应用程序访问数据库执行SQL时,首先需要和数据库服务器建立连接,然后执行SQL语句,最后再关闭数据库连接。而数据库连接相当于是一个资源,创建连接时需要进行TCP三次握手的操作,关闭连接时需要进行TCP四次挥手的操作。
如果每次执行SQL都需要进行连接的创建和销毁,无疑会严重降低SQL执行的效率。因为需要花费大量的资源进行TCP连接的频繁创建和销毁过程。
连接池相当于是连接的容器,在应用程序启动时初始化指定数量的连接供应用程序使用,那么在每次执行SQL时只需要从连接池中获一个连接对象直接执行SQL即可,执行完SQL将连接还给连接池即可。应用程序完全不用关心连接的创建和销毁过程。

1.3、常用连接池

连接池的种类有很多,比如数据库连接池包括DBCP、C3P0、Druid、HikariCP等开源连接池,DBCP和C3P0两个连接池整体性能较差,Druid和HikariCP两个连接池性能很好,目前最热门连接池是Druid和HikariCP连接池。Springboot2.0默认使用数据库连接池为HikariCP连接池另外还有redis连接池、mongo连接池等常用连接池。

二、连接池的作用

2.1、减少创建和销毁效率的消耗

对于连接的创建和销毁全部由连接池来维护,应用程序即拿即用无需进行连接的操作,减少大量的TCP连接的时间消耗。提升了整体的和服务器通信的效率。以redis连接为例,假设有10个客户端想要执行get key命令

在没有连接池的情况下:就需要进行10次TCP三次握手和四次挥手,并且执行10次get key命令

在使用连接池的情况下:只需要进行1次TCP三次握手和四次挥手,再执行10次get key命令

很显然采用连接池的方式在客户端时候的时候基本上是不会有TCP握手和挥手的过程,只需要发送命令即可.

2.2、数据库连接数可控

没有连接池的话,那么每个客户端访问数据库都需要创建一个连接,对于并发量较高的系统来说,就有可能会同时创建成千上万的数据库连接,很容易就导致数据库的连接数不足而连接失败。
而通过连接池的话就可以使数据库的连接始终保持在固定的数量,不会出现由于连接数过多而给数据库造成较大的性能消耗。
以数据库连接为例,假设同时有1000个用户正在执行查询操作
在没有连接池的情况下:同时创建了1000个数据库连接,数据库同时执行1000个SQL查询,由于数据库是并行的但是CPU是串行,所以1000个SQL会依次执行且会导致不停的CPU切换,导致1000个查询都会很慢,另外还有可能导致数据库压力骤增.
在使用连接池的情况下:假设连接池的大小为100,那么同时有100个连接执行SQL,其他连接在排队等待,对于数据库而言,少量的连接会在短时间内就可以执行完成,虽然有部分在排队,但是也是会很快能依次执行完成,因为数据库CPU上下文切换很少
所以在使用连接池的情况下,客户端同时访问数据库的连接数是有数量限制的,数据库同时不会出现执行大量SQL的情况,不会出现数据库压力很大的情况.

三、连接池的工作流程

1、连接池建立

连接池建立需要初始化一个存储连接的容器,并且初始化指定数量的连接,方便应用程序立即使用。

2、连接的管理

连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是:

当客户请求连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户。
当客户释放连接时,先判断该连接的引用次数是否超过了规定值,如果超过就从连接池中删除该连接,否则保留为其他客户服务。
该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销。

3、连接池关闭

当应用程序退出时,关闭连接池中所有的连接,释放连接池相关的资源,该过程正好与创建相反。

四、连接池的实现细节

要实现一个连接池,首先需要梳理清楚连接池的工作内容以及连接池会遇到的问题。实现连接池大致需要实现的功能大致如下:

4.1、连接容器的实现?

既然是连接池,所以就需要有池子的概念,创建了连接之后需要有一个容器用于存放连接,一般可采用的方式是数组或者是链表这两种数据结构.但是由于连接池的特性是增删连接较少,而查询获取连接的场景较多,所以采用数组的方式是最适合的。

4.2、连接池的连接初始化?

对于连接池而言,初始化的时候通常会初始化指定数量的连接,这样在连接池初始化之后就能立即可以提供连接给客户端使用,否则在初始化启动之后客户端访问的时候还是需要走创建连接的流程

4.3、连接池连接数的动态变化?

对于客户端访问资源的频率通常不是一成不变的。比如淘宝网站,用户访问的频率通常是周末访问频率比工作日访问频率要高,工作日中上班期间访问频率较低而下班期间访问较高。
所以客户端对于连接的需求也会随着用户使用习惯的规律而动态变化。如果连接池的连接数量一成不变就会导致高峰期间连接不够用,低峰期间连接长时间空闲。
所以连接池需要随着需求量的增长而动态增加连接,随着需求量的下降而动态删除连接

4.4、连接空闲检测机制?

当连接池中的连接长时间空闲,就应该将空闲的连接进行关闭,避免造成不必要的资源浪费,所以连接池需要有一个定时任务定期对所有连接进行是否为空闲的检测,并且需要检测空闲的时长,
因为如果仅判断空闲不判断空闲时长的话,很容易将刚刚被释放的连接当作是空闲的连接给关闭。所以需要对于空闲的连接要判断下当前连接空闲了多长时间,只有空闲了较长时间的连接才应该被关闭

4.5、强制回收机制?

当客户端获取连接之后,如果占用时间较长或者忘记关闭连接,就会导致连接一直被占用的,这样就会导致连接池中的连接很快被消耗完。所以需要有一种强制回收连接的机制,当连接被客户端占用的时间较长
时强制回收该连接。被回收连接的客户端如果需要连接的话就需要重新申请连接。

4.6、多线程下的并发问题?

连接池的维护需要考虑到诸多的并发问题,比如:
1.初始化连接池的并发问题,初始化连接池需要保证只能由一个线程来初始化,避免多个线程同时初始化连接池
2.创建连接的并发问题,动态创建连接时,需要并发控制,避免同时创建过多连接且超过了连接池上限,比如连接池上限为100个连接,当前共有99个连接正在被使用,此时又两个客户端来创建连接,如果不做并发控制就会导致两个客户端都创建连接成功导致连接数超过来了上限
3.销毁连接的并发问题,和创建连接问题类似,销毁连接如果不做并发控制容易导致连接数被销毁的少于连接池的最小连接数
4.获取连接的并发问题,获取连接的并发是连接池最重要的并发控制,因为需要避免两个客户端同时获取到了同一个连接的问题

五、总结

连接池的种类有很多,只要是有连接的场景就可以使用连接池,连接池的使用使得连接可以复用,提高了连接具体的工作效率。并且避免了连接过多导致服务器负载过高的问题。虽然不同的中间件服务器提高了不同的连接,但是连接池的工作原理基本上都是相通的,理解了一种连接池的工作原理,其他类型的连接池基本上都可以很快的理解透彻。

相关连接:

数据库连接池之Druid连接池源码解析

原文地址:https://www.cnblogs.com/jackion5/p/14169454.html