2.1 为什么使用数据库连接池
一些初学者可能会有这样的疑问:为什么要使用数据库连接池?一些中高级的朋友也许会问:数据库连接池本身就是一个很简单的东西,只是内存里放了一个存连接的池,使用的时候拿,用完了就归还,有什么好讲的,又有何意义,居然还能写出一本书来?我曾经也有这样的疑问,但是在写这本书的一年时间内,我认为数据库连接池麻雀虽小五脏俱全,了解它还是很有必要的。
如图2-1所示,数据库连接池存在于数据库和用户之间,是一种管理数据库连接的中间件。用户通过数据库连接池获取连接,从而使用数据库的资源,并执行通用的“增删改查”操作。
图2-1 数据库连接池示意图
数据库连接池在百度百科中的定义是:数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个;释放空闲时间超过最大空闲时间的数据库连接,来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
那么为什么使用数据库连接池会明显提高数据库操作的性能呢?下面我们结合TCP的3次握手和4次挥手,对不使用数据库连接池和使用数据库连接池两种场景分别进行介绍。
让我们一起重温大学岁月,那些年在学习编程语言的时候,为了让学生能够快速学会数据库增删改查操作,教材里基本上都采用不使用数据库连接池的讲解和代码示例。经典的JDBC连接数据库的步骤大致是这样的:加载JDBC驱动、创建数据库连接、创建preparedStatement、执行SQL语句、遍历结果集、关闭资源。这样的教科书式的流程可以通俗理解为:应用程序通过DataSource通过数据库驱动来创建一个数据库连接,再通过TCP进行数据库的读写,最后关闭数据库连接。
以访问MySQL为例,执行一个SQL语句的完整TCP流程共经历TCP三次握手建立连接、MySQL三次握手认证、SQL语句执行、MySQL关闭、TCP四次挥手关闭连接5个步骤,如图2-2所示。
图2-2 执行一次SQL语句的完整TCP流程
这样的传统方式比较适合教科书,但是却不适合企业实际生产。它存在的主要问题列举如下:
1)创建连接和关闭连接的过程比较耗时,并发时系统会变得很卡顿。
2)数据库同时支持的连接总数是有限的,如果并发量很大,那么数据库连接的总数就会被消耗光,增加数据库的负载,新的数据库连接请求就会失败。这样就会极大地浪费数据库的资源,极易造成数据库服务器内存溢出、宕机。
3)为了执行一条SQL,却产生了很多我们并不关心的网络IO。
4)应用如果频繁地创建连接和关闭连接,会导致JVM临时对象较多,GC频繁。
5)频繁关闭连接后,会出现大量TIME_WAIT的TCP状态(在2个MSL之后关闭),这点很棘手。这个问题在第1章中已经结合实践调优经验详细说明过。
6)应用的响应时间及QPS较低。
使用数据库连接池之后,产生最直接的改变就是就是基本上除了第1次访问的时候需要建立连接以外,之后的访问只需复用已有的连接而不是重新建立一个。
数据库连接池带来的优点列举如下:
1)资源重用更佳。由于数据库连接得到复用,减少了大量创建和关闭连接带来的开销,也大量减少了内存碎片和数据库临时进程、线程的数量,整体系统的运行更加平稳。
2)系统调优更简便。由于频繁关闭连接会出现TCP大量TIME_WAIT状态,如第1章的案例所描述的那样,TIME_WAIT的调优非常繁琐,使用了数据库连接池以后,由于资源重用,大大减少了频繁关闭连接的开销,大大降低TIME_WAIT的出现频率。当然,数据库连接池也有它自己独特的配置参数,这些参数如何调优本书后续章节会详细介绍。
3)系统响应更快。数据库连接池在应用初始化的过程中一般都会提前准备好一些数据库连接,业务请求可以直接使用已经创建的连接而不需要等待创建连接的开销。初始化数据库连接配合资源重用,使得数据库连接池可以大大缩短系统整体响应时间。
4)连接管理更灵活。数据库连接池作为一款中间件,除了扮演有界缓冲的角色以外,在统一的连接管理上同样可以做很多文章。用户可以自行配置连接的最小数量、最大数量、最常空闲时间、获取连接超时间、心跳检测等。另外,用户也可以结合新的技术趋势,增加数据库连接池的动态配置、监控、故障演习等一系列实用的功能。
Vlad Mihalcea在他的著作《High-Performance Java Persistence》中介绍过一个试验,可视化显示建立和关闭数据库连接的累积开销,他比较了打开和关闭4种不同的RDBMS与HikariCP数据库连接池的1000个数据库连接,如图2-3所示。可以看出,使用数据库连接池HikariCP解决方案以后连接获取时间得到了大大的缩短。通过减少连接获取间隔,整个事务响应时间也会缩短。
图2-3 有无数据库连接池HikariCP的连接建立开销对比
综上所述,这就是我们要在实际生产中使用数据库连接池的理由。