基本概念

定义

  • 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个;
  • 释放空闲时间超过最大空闲时间的数据库连接,来避免因为没有释放数据库连接而引起的数据库连接遗漏。

传统过程

经典的 JDBC 连接数据库的大致步骤:

  • 加载 JDBC 驱动
  • 创建数据库连接
  • 创建 preparedStatement
  • 执行 SQL 语句
  • 遍历结果
  • 关闭数据库连接

在网络层面,以访问 MySQL 为例,执行一个 SQL 语句的完整 TCP 流程包括:

  • TCP 三次握手建立连接
  • MySQL 三次握手认证
  • SQL 语句执行
  • MySQL 关闭
  • TCP 四次挥手关闭连接
NAME

存在问题

这种传统连接方式的问题有:

  • 创建和关闭连接的过程比较耗时,并发时系统会变得卡顿。
  • 数据库同时支持的连接数有限,如果并发量很大,数据库的连接数则会被耗尽,增加了数据库负载,新的数据库连接请求将会失败。
    • 这会极大的浪费数据库资源,极易造成数据库服务内存溢出、宕机。
  • 为了执行一条 SQL,却产生了很多我们并不关心的网络 IO。
  • 应用如果频繁的创建和关闭连接,会导致 JVM 临时对象较多,GC 频繁。
  • 频繁关闭连接后,会出现大量 TIME_WAIT 的 TCP 状态(在 2 个 MSL 之后关闭)。
  • 应用的响应时间和 QPS 较低。

池化优点

使用数据库连接池后,最直接的改变就是仅需在首次访问时创建连接,之后的访问直接可以复用已有连接。

  • 资源重用。由于数据库连接得到复用,减少了大量创建和关闭连接带来的开销,也减少了内存碎片和数据库临时进程、线程的珊瑚粮,整体系统的运行更加平稳。
  • 系统调优更简便。TIME_WAIT 的调优非常繁琐,使用了数据库连接池以后,由于资源重用,大大减少了频繁关闭连接的开销,大大降低 TIME_WAIT 的出现频率。
  • 系统响应更快。数据库连接池在应用初始化的过程中一般都会提前准备好一些数据库连接,业务请求可以直接使用已经创建的连接而不需要等待创建连接的开销。初始化数据库连接配合资源重用,使得数据库连接池可以大大缩短系统整体响应时间。
  • 连接管理更灵活。数据库连接池作为一款中间件,除了扮演有界缓冲的角色以外,在统一的连接管理上同样可以做很多文章。用户可以自行配置连接的最小数量、最大数量、最常空闲时间、获取连接超时间、心跳检测等。另外,用户也可以结合新的技术趋势,增加数据库连接池的动态配置、监控、故障演习等一系列实用的功能。

实现原理

在系统初始化的时候,在内存中开辟一片空间,将一定数量的数据库连接作为对象存储在对象池里,并对外提供数据库连接的获取和归还方法。

用户访问数据库时,并不是建立一个新的连接,而是从数据库连接池中取出一个已有的空闲连接对象;使用完毕归还后的连接也不会马上被关闭,而是由数据库连接池统一管理回收,为下一次借用做好准备。

如果由于高并发请求导致数据库连接池中的连接被借用完毕,其他线程就会等待,直到有连接被归还。

整个过程中,连接并不会被关闭,而是源源不断地循环使用,有借有还。数据库连接池还可以通过设置其参数来控制连接池中的初始连接数、连接的上下限数,以及每个连接的最大使用次数、最大空闲时间等,也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。

基本构成

数据库连接池的核心功能是建立和释放连接。通常完整的连接池实现会提供更多功能:

  • 并发优化:锁性能优化,甚至无锁
  • 连接数控制:不同的系统对连接数的要求不同
  • 监控:提供管理机制来监视连接数量或用量
  • 外部配置
  • 资源重用
  • 检测/容灾:面对网络、时间问题的自愈
  • 多库:不同的数据库、不同的用户与密码、分库分表
  • 事务处理:对数据库的操作符合 ALL-ALL-NOTHING 原则
  • 定时任务:空闲检查、最小连接数控制
  • 缓存:如 PSCache 等避免 SQL 重复解析
  • 异常处理:对 JDBC 异常的统处理
  • 组件维护:连接状态、JDBC 封装维护
NAME