数据库连接池原理

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

为什么使用数据库连接池

传统方式的缺点:

  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 连接池特有:监控连接被借用后是否在规定时间内归还,用于发现代码中未正确关闭连接的错误。

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

相关推荐
r i c k22 分钟前
数据库系统学习笔记
数据库·笔记·学习
野犬寒鸦37 分钟前
从零起步学习JVM || 第一章:类加载器与双亲委派机制模型详解
java·jvm·数据库·后端·学习
IvorySQL1 小时前
PostgreSQL 分区表的 ALTER TABLE 语句执行机制解析
数据库·postgresql·开源
·云扬·2 小时前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
IT邦德2 小时前
Oracle 26ai DataGuard 搭建(RAC到单机)
数据库·oracle
惊讶的猫2 小时前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
不爱缺氧i2 小时前
完全卸载MariaDB
数据库·mariadb
纤纡.2 小时前
Linux中SQL 从基础到进阶:五大分类详解与表结构操作(ALTER/DROP)全攻略
linux·数据库·sql
jiunian_cn3 小时前
【Redis】渐进式遍历
数据库·redis·缓存
橙露3 小时前
Spring Boot 核心原理:自动配置机制与自定义 Starter 开发
java·数据库·spring boot