数据库连接池原理

纯概念, 目前暂未看过源码

为什么使用数据库连接池

传统方式的缺点:

  1. 资源消耗大:创建数据库连接是一个非常"昂贵"的操作。它需要经历TCP三次握手、数据库权限验证、连接状态初始化等,消耗大量的CPU和内存资源。

  2. 响应速度慢:每次请求都要等待连接建立,导致整体响应时间变长。

  3. 难以管理连接数:如果并发请求量很大,可能会瞬间创建大量连接,耗尽数据库资源,导致数据库崩溃。

连接池的解决方案:

连接池的核心思想是空间换时间。在系统初始化时,就预先创建一定数量的数据库连接并放入一个"池子"中管理。当应用程序需要时,直接从池中获取一个空闲连接,使用完毕后归还给池子,而不是真正关闭它。

优点:

  • 减少连接创建开销:连接的初始化工作只在池子启动时完成一次,后续请求直接复用,极大提升了性能。

  • 控制资源使用:池子可以限制最大连接数,防止数据库过载。

  • 统一连接管理:池子可以管理连接的生命周期,包括检测无效连接、自动重连等。

数据库连接池实现过程

DB连接池中核心组件

  1. 线程安全的并发队列 (如BlockingQueue)来存放空闲的数据库连接
  • 空闲连接队列 (idleConnections):存放当前可用、未被使用的连接。

  • 活跃连接集合 (activeConnections):存放正在被应用程序使用的连接(用于监控和管理)。

工作流程

1.链接初始化

在应用程序启动或连接池首次被使用时,根据配置的initialSize(初始连接数),创建一定数量的数据库连接,并将它们放入空闲连接队列

  1. 获取连接阶段

当应用程序调用 DataSource.getConnection() 时:

  1. 检查空闲队列 :首先尝试从idleConnections队列中取出一个连接。

  2. 创建新连接(如果需要)

    • 如果空闲队列为空,且当前总连接数(活跃+空闲)小于maximumPoolSize(最大连接数),池子会创建一个新的物理连接。

    • 如果总连接数已达上限,请求会等待(在maxWaitTime时间内),直到有其他连接被归还。

  3. 验证连接有效性 :从池中取出的连接,在交给应用程序之前,可能会用一条简单的SQL(如SELECT 1)进行有效性验证(取决于testOnBorrow配置),确保连接没有被数据库端意外关闭。

  4. 标记为活跃 :将连接从idleConnections移到activeConnections,然后返回给应用程序。

  5. 使用连接阶段

应用程序拿到连接后,像普通连接一样执行SQL操作。但它实际上是通过代理完成SQL操作。

  1. 归还连接阶段

当应用程序调用 Connection.close() 时:

  1. 代理拦截 :代理连接的close()方法被调用,它并不会真正关闭底层的物理连接。

  2. 状态重置 :连接池会回滚可能未提交的事务、重置连接的相关配置(如autoCommit状态)。

  3. 回归池子 :将该物理连接从activeConnections集合中移除,重新放回idleConnections队列,等待下一次被获取。

其他重要管理机制

  • 空闲连接回收 :有一个后台线程定期扫描idleConnections队列。如果某个连接的闲置时间超过了idleTimeout,则将其真正关闭,以节省数据库资源。

  • 失效连接检测 :后台线程会定期检查池中的连接是否仍然有效(例如,通过执行SELECT 1),如果连接由于网络问题或数据库重启而失效,则将其丢弃并补充新的连接。

  • 连接泄漏回收 :监控那些被应用程序借出但长时间未归还的连接(通过leakDetectionThreshold配置)。如果超过阈值,连接池可能会记录警告日志甚至强制回收该连接。

工作流程

数据库连接池和线程池关系

两者"池化"思想上是相通的,但由于它们管理的资源性质完全不同,因此在参数配置的侧重点和考量维度上存在显著差异。

  • 线程池 :管理的是 CPU 计算资源。线程是 JVM 层面的执行单元,其创建和销毁消耗的是 CPU 和内存。

  • 数据库连接池 :管理的是 外部稀缺资源。数据库连接背后是网络连接、数据库服务器本身的会话、内存和CPU。数据库能同时支撑的连接数是硬性限制,远比应用服务器的线程数要少。

参数类别 线程池 (如 ThreadPoolExecutor) 数据库连接池 (如 HikariCP, Druid) 核心差异说明
核心池大小 corePoolSize minimumIdle (如 HikariCP) 思路相似 :希望保持的常驻资源数量。但线程池常基于CPU核心数,连接池常设一个较小值以节省DB资源
最大池大小 maximumPoolSize maximumPoolSize 差异巨大 : • 线程池 :可达数百甚至数千,取决于业务类型和服务器性能。 • 连接池通常很小 (如 20-100),受限于数据库的max_connections。这是最关键的区别
空闲资源处理 keepAliveTime + unit idleTimeout / maxLifetime 目标不同 : • 线程池 :回收超出核心数 的空闲线程,快速释放内存。 • 连接池 :回收任何空闲 连接(防空闲占用),并有最大生命周期(强制刷新,防止网络僵死)。
任务/连接队列 workQueue (如 LinkedBlockingQueue) 通常无显式配置,内部实现 角色不同 : • 线程池队列 :缓冲待执行的任务 。队列大小直接影响任务提交策略(拒绝 or 等待)。 • 连接池 :没有"任务队列",只有空闲连接的物理队列 。获取连接的等待行为由connectionTimeout控制。
资源创建/销毁 ThreadFactory 无直接对应参数,由驱动完成 线程池 :可自定义线程创建方式(如线程名、优先级)。 • 连接池:连接创建由JDBC驱动和数据库协议决定,池只负责调用。
资源验证 无此概念 connectionTestQuery validationTimeout 连接池特有 :由于连接是网络资源,可能因网络问题或数据库重启而失效,因此必须 有有效性检查机制(如SELECT 1)。线程是本地资源,无需验证。
获取超时 无(提交任务时,队列满则触发拒绝策略) connectionTimeout 连接池特有:定义获取一个连接的最大等待时间。防止线程在无法获取连接时被无限期阻塞。这是非常重要的保护机制。
泄漏检测 无此概念 leakDetectionThreshold 连接池特有:监控连接被借用后是否在规定时间内归还,用于发现代码中未正确关闭连接的错误。

配置线程池时你想的是"我的机器能同时干多少活 ";而配置数据库连接池时你想的是"我的数据库能同时承受多少访问,我如何确保每次访问用的连接都是好的"。

相关推荐
程序新视界3 小时前
MySQL中的数据去重,该用DISTINCT还是GROUP BY?
数据库·后端·mysql
谱写秋天3 小时前
软考-系统架构设计师 关系数据库详细讲解
数据库·系统架构·软考架构师
檀越剑指大厂3 小时前
平替MongoDB:金仓多模数据库助力电子证照国产化实践
数据库·mongodb
cpsvps4 小时前
环境变量管理于美国服务器多环境部署的实施标准
运维·服务器·数据库
IvorySQL4 小时前
【生态再升级】IvorySQL 4.5 与银河麒麟高级服务器操作系统V11完成适配认证!
运维·服务器·数据库·postgresql·ivorysql
CYX_cheng4 小时前
sqlsugar sqlite
数据库·sqlite
茉莉玫瑰花茶5 小时前
Redis - Bitmap 类型
数据库·redis·缓存
IvorySQL5 小时前
聚焦六大功能:PostgreSQL 18 新特性深度解析
数据库·postgresql·开源
饿了我会自己捡代码吃5 小时前
【MySQL】使用C/C++链接mysql数据库
c语言·数据库·mysql