连接池与会话与连接泄漏与保活

共 20 题
📑 题目列表 20 题
#
★★★

1. MySQL 中 wait_timeout、interactive_timeout 的影响?

MySQL 中 wait_timeout 与 interactive_timeout 分别是控制什么?它们对连接池里的空闲连接有什么影响?

  • 两个超时参数的含义与作用对象
  • 对连接池空闲连接被服务端主动断开的影响
  • 与连接池保活、validation 的配合

wait_timeout 控制非交互式连接(如 JDBC、ODBC 等程序连接)在空闲超过该秒数后被服务端关闭;interactive_timeout 控制交互式连接(如 mysql 命令行客户端)的空闲超时。两者默认都是 8 小时(28800 秒)。连接池中长时间空闲的连接如果超过 wait_timeout,会被 MySQL 服务端主动断开,但客户端并不感知,直到下次使用该连接发起请求时才会收到 server has gone away 之类的错误。所以连接池必须配合保活(heartbeat/validation query)在空闲连接被服务端断开前主动回收或剔除。

关键在于服务端主动断开早于客户端感知,造成"死连接"(stale connection)被复用。wait_timeout 只针对非交互式连接,因此程序连接池受其影响;过低会导致连接频繁被回收,过高会导致服务端大量空闲连接占用资源。

SHOW VARIABLES LIKE 'wait_timeout';
SHOW VARIABLES LIKE 'interactive_timeout';
SET SESSION wait_timeout = 300;
#
★★★

2. PostgreSQL 中连接与 session 的关系,连接池复用与 session 状态?

在 PostgreSQL 中,一个连接(connection)与一个 session 是什么关系?连接池复用连接时,session 状态(如 SET 变量、临时表、prepared statement)如何处理?

  • PostgreSQL 中连接与会话一一对应的关系
  • session 级状态(SET、临时表、预编译语句)在连接复用时的残留
  • 连接池如何清理 session 状态

在 PostgreSQL 中,每个后端进程(backend process)对应一个连接,一个连接就是一个个 session,两者一一对应。session 级状态包括 SET 配置参数、临时表、LISTEN/NOTIFY、prepared statement 等,这些状态在连接被连接池复用给另一个业务请求时不会自动清除,可能造成状态残留(例如上一个会话 SET 的时区、schema 影响下一个业务)。连接池需要在取出连接时执行清理(如 reset 语句、标准化的状态重置),或通过 application_name、对连接做 reset 后再复用。

PostgreSQL 没有像 MySQL 那样区分"连接 vs 会话",一个连接就是一个 session,因此复用连接必须主动重置 session 状态,否则会污染后续请求,这是连接池设计的关键点。

SET TIME ZONE 'UTC';
SET search_path TO my_schema;
CREATE TEMP TABLE tmp_x (id int);
-- PgBouncer 等连接池在复用时会执行 DISCARD ALL 清理会话状态
DISCARD ALL;
#
★★★

3. 主流连接池,HikariCP、Druid、c3p0、DBCP 的对比?

请对比 HikariCP、Druid、c3p0、DBCP 这几款主流数据库连接池的优缺点与适用场景?

  • 各连接池的性能表现
  • 特性丰富度(监控、SQL 防火墙、拦截)
  • 选型依据

HikariCP 性能最高、字节码精简、零依赖,是 Spring Boot 2.x 默认连接池,适合追求极致性能和对监控要求不高的场景;Druid 是阿里开源的连接池,功能最丰富,内置监控统计面板、SQL 防火墙、慢 SQL 拦截、连接泄漏检测等,适合需要可视化监控和安全性管控的中大型应用;c3p0 与 DBCP 是较老的传统连接池,功能简单、性能一般、多有已知缺陷,如今已很少使用,仅用于遗留项目。选型上:默认推荐 HikariCP,需要监控与安全管控时选 Druid。

性能对比上 HikariCP 明显占优,因为它采用极简代码、FastList 等优化;Druid 的优势在于功能丰富但额外开销略大。c3p0/DBCP 已过时。

#
★★★

4. 数据库连接池(Connection Pool)的必要性,减少 TCP/握手开销、限制连接数?

为什么需要数据库连接池?它如何减少 TCP 与握手开销并限制连接数?

  • 连接建立的高成本(TCP 三次握手 + 认证 + 协议协商)
  • 连接池复用连接减少开销
  • 限制并发连接数避免数据库资源耗尽

数据库连接建立成本很高,包括 TCP 三次握手、TLS 握手、认证(如 MySQL caching_sha2_password、Pg SCRAM)、协议协商等,每次请求都新建连接会带来巨大延迟与开销。连接池在应用启动时预先建立并维护一批连接,请求时从池中获取、用完归还,从而复用连接、大幅降低开销。同时连接池可以限制最大连接数(maxActive/maximumPoolSize),避免业务并发过大时数据库连接数失控、耗尽数据库连接资源。

连接池的核心价值是"复用昂贵的连接资源"与"限流保护后端"。限制连接数对数据库稳定性至关重要,因为数据库连接数受 max_connections 限制,连接过多会导致数据库拒绝新连接。

#
★★★

5. 连接池的关键参数,initialSize、minIdle、maxActive、maxWait?

请解释连接池关键参数 initialSize、minIdle、maxActive、maxWait 的含义与作用?

  • 各参数的中文含义
  • 参数对池行为的影响
  • Druid 与 HikariCP 参数命名差异

initialSize 是连接池启动时初始创建的连接数;minIdle 是最小空闲连接数,低于该值时池会补充新连接;maxActive(Druid)对应 HikariCP 的 maximumPoolSize,是池中最大连接数,超过则新请求进入等待;maxWait 是获取连接的最大等待时间(毫秒),超过则抛异常。它们的配合关系是 initialSize ≤ minIdle ≤ maxActive,合理的取值能兼顾启动速度和资源占用。HikariCP 中主要用 minimumIdle 与 maximumPoolSize,没有 initialSize 概念(启动即建 minimumIdle 数量的连接)。

这些参数决定池的大小与弹性。maxActive 过大浪费数据库连接资源,过小则在高并发下排队;maxWait 防止无限等待导致请求堆积。

#
★★★

6. 连接池的预热(Warmup)与验证(Validation)?

连接池的预热(Warmup)与验证(Validation)分别是什么?如何实现?

  • 预热在应用启动时预先建立连接
  • 验证机制(test-while-idle、test-on-borrow 等)
  • 验证查询(validation query)的选择

预热(Warmup)指应用启动时通过 initialSize 预先建立一批连接,避免流量高峰时首次请求才建连造成延迟尖峰。验证(Validation)指在使用前或使用中检测连接是否有效,常用方式有 test-on-borrow(取出时验证)、test-while-idle(空闲时定时验证)、test-on-return(归还时验证)。验证查询通常用轻量语句如 MySQL 的 SELECT 1、Oracle 的 SELECT 1 FROM DUAL;HikariCP 默认使用 connectionTestQuery 或自动检测。验证能及时发现并剔除失效连接,避免复用到死连接。

预热解决启动期建连延迟,验证解决"连接被服务端断开后仍被复用"的问题。test-on-borrow 最可靠但每次多一次网络往返,test-while-idle 更高效但可能复用尚未被验证的失效连接。

SELECT 1;          -- MySQL
SELECT 1;          -- PostgreSQL
SELECT 1 FROM DUAL; -- Oracle
#
★★★

7. 连接池大小与数据库 max_connections 的规划,多应用实例 × 池大小的数学与过载保护

如何规划连接池大小与数据库 max_connections?多应用实例部署时如何计算总连接数并做保护?

  • 连接池大小与数据库连接上限的数学关系
  • 多实例 × 池大小的乘积
  • 过载保护与预留余量

规划时需保证:所有应用实例的连接池总和 ≤ 数据库 max_connections,并预留余量给运维、DBA、采集监控等连接。例如数据库 max_connections=1000,预留 20%,则应用可用约 800;若 4 个实例,每个实例池大小应 ≤ 200。池大小并非越大越好,过大反而导致数据库连接浪费、锁竞争增加。HikariCP 官方建议用"CPU 核心数 ×2 + 磁盘 IO 数"的经验公式,但更实际的做法是按并发与压测确定。同时连接池可设置 maxWait 超时作为过载保护,避免请求无限等待。

核心是"多实例 × 单实例池大小"必须低于数据库 max_connections 并留余量,否则数据库会拒绝连接导致雪崩。这是容量规划的核心问题。

#
★★

8. PgBouncer、ProxySQL 等外部连接池的实现?

PgBouncer、ProxySQL 等外部(代理)连接池是如何实现的?和进程内连接池有何区别?

  • 外部连接池的架构(位于应用与数据库之间)
  • 池模式(session/transaction/statement)
  • 与进程内连接池的差异

PgBouncer(PostgreSQL)和 ProxySQL(MySQL)是独立部署在应用与数据库之间的代理连接池,应用连接代理、代理再连接数据库。它们的核心价值是让应用持有少数连接,数据库端连接数被代理集中管理,从而大幅降低数据库连接数。PgBouncer 支持 session、transaction、statement 三种池模式;ProxySQL 支持读写分离、故障转移、查询缓存等。与进程内连接池(如 HikariCP)相比,外部池能跨应用实例共享连接、降低数据库连接压力,但增加一次网络跳转与代理层的运维复杂度。

外部连接池把"连接复用"从单进程提升到全局,适合连接数繁多的微服务场景。transaction 池模式在事务结束后立即归还连接,用连接数最小化,但临时表、prepared statement 等会话状态会受影响。

#
★★

9. MySQL 中 wait_timeout 与连接保活的协同?

MySQL 的 wait_timeout 与连接池保活机制如何协同工作?

  • wait_timeout 决定服务端断开空闲连接的阈值
  • 保活需在阈值内完成心跳
  • 协同策略参数

wait_timeout 决定了服务端断开空闲连接的阈值,连接池的保活(如 heartbeat SQL、test-while-idle 周期)必须在小于 wait_timeout 的周期内执行,以保证空闲连接在服务端断开前被"激活"。一般做法是让保活周期远小于 wait_timeout(如 wait_timeout=8h,保活周期 30s~1min),或直接调小 wait_timeout 同时配合保活。这样既避免了服务端大量空闲连接存留,又保证池中连接有效。

协同要点是"保活周期 < wait_timeout"。若保活周期大于 wait_timeout,连接仍会被服务端断开,保活失去意义。

#
★★

10. 连接保活(Keep-Alive)的实现,心跳、空闲连接检测?

连接保活(Keep-Alive)如何实现?心跳与空闲连接检测分别是什么?

  • 心跳机制的实现
  • 空闲连接检测(test-while-idle)
  • 保活 SQL 的选择

连接保活常用两种方式:一是定时发送心跳 SQL(如 SELECT 1)主动维持连接活跃,防止被服务端或防火墙(如 NAT 的 idle timeout)断开;二是空闲连接检测(test-while-idle),连接池定时轮询空闲连接执行验证查询,发现失效则剔除。两者本质都是在空闲连接被外部断开前主动探测。HikariCP 的 keepaliveTime、Druid 的 testWhileIdle 与 timeBetweenEvictionRunsMillis 都用于实现这类保活。

心跳是"主动维持",空闲检测是"主动验证",都用于对抗连接被服务端/网络设备静默断开。NAT 防火墙的 idle timeout 往往比数据库 wait_timeout 更小,是保活周期需考虑的因素。

#
★★

11. 连接泄漏的检测,Druid 的 leakDetectionThreshold?

如何检测连接泄漏?Druid 的 leakDetectionThreshold 如何工作?

  • 连接泄漏的概念
  • leakDetectionThreshold 参数
  • 泄漏检测的引出与告警

连接泄漏指业务代码获取连接后未正确归还,导致池中连接被耗尽。Druid 通过 leakDetectionThreshold 参数实现检测:当连接从池中借出超过该毫秒数仍未归还时,Druid 会记录告警日志(打印出获取连接的堆栈信息),便于定位泄漏代码。HikariCP 也提供类似机制(leakDetectionThreshold 属性),检测到泄漏会打印堆栈。这类检测有性能开销,通常只在排查泄漏时开启。

泄漏检测通过"借出超时"来近似判断泄漏,实际上是"疑似泄漏"的堆栈快照,能精确定位到获取连接但未归还的代码位置,是治理连接耗尽问题的重要手段。

#
★★

12. 连接泄漏(Connection Leak)的成因,未关闭连接、异常未释放?

连接泄漏(Connection Leak)的成因有哪些?如何避免?

  • 未关闭连接
  • 异常时未释放(finally 缺失)
  • 避免方式(try-with-resources、finally 关闭)

连接泄漏的主要成因包括:业务代码获取连接后忘记关闭;异常抛出时未执行 finally 中的关闭逻辑导致连接未归还;连接被存放到静态/长生命周期对象中长时间持有;长时间运行的事务未提交/回滚导致连接被占用。避免方式:使用 try-with-resources 或 finally 块保证关闭、统一通过连接池管理、设置事务超时、开启泄漏检测监控。连接泄漏最终会导致连接池耗尽、应用不可用。

连接泄漏的本质是"获取与归还不对等"。异常路径是最容易漏掉关闭的地方,因此 finally 或 try-with-resources 是标准解法。

#
★★

13. Druid 连接池的核心特性,监控统计、SQL 防火墙、慢 SQL 拦截与连接泄漏检测如何工作?与 HikariCP 相比现在还有哪些选型价值?

Druid 连接池的监控统计、SQL 防火墙、慢 SQL 拦截、连接泄漏检测如何工作?与 HikariCP 相比现在还有哪些选型价值?

  • Druid 的监控统计实现
  • SQL 防火墙与慢 SQL 拦截
  • 连接泄漏检测

Druid 通过内置的 Filter 体系(StatFilter、WallFilter 等)实现功能:StatFilter 采集 SQL 执行统计(执行次数、耗时、错误、SQL 明细)并输出到监控页面(Druid Monitor);WallFilter 是 SQL 防火墙,能拦截危险 SQL(如注释符绕过、禁止的 DDL)、防御 SQL 注入攻击;慢 SQL 拦截通过 setSlowSqlMillis 记录超过阈值的 SQL;连接泄漏检测通过 leakDetectionThreshold 打印堆栈。相比之下 HikariCP 更专注性能,功能极简。Druid 的选型价值在于内置一体化监控与安全管控,适合需要可视化监控、SQL 治理和安全审计的中大型应用,尤其在国内企业级项目中使用广泛。

Druid 的核心优势是"一站式":监控 + 防护 + 诊断开箱即用。HikariCP 是纯性能导向。对于需要 SQL 审计、慢 SQL 分析与防注入的团队,Druid 仍有很强的选型价值。

#
★★

14. HikariCP 的优势来源,字节码精简、FastList 与并发集合优化如何降低获取连接的延迟,其参数调优(maximumPoolSize 等)的关键点?

HikariCP 的优势来源是什么?字节码精简、FastList、并发集合优化如何降低获取连接延迟?参数调优关键点是什么?

  • HikariCP 的优化手段(字节码精简、FastList、无锁/并发集合)
  • 获取连接延迟的降低原理
  • maximumPoolSize 等参数调优

HikariCP 的性能优势来自多个方面:使用字节码注入(如 Javassist)精简生成的代理类,减少方法调用开销;自研 FastList 替代 ArrayList 在归还连接时避免索引查找;使用并发集合(如 blocked 队列、自定义并发数据结构)减少锁竞争;减少自带代码量,连接获取路径极简。这些优化共同降低了获取连接与归还连接的系统开销。参数调优关键点:maximumPoolSize 是核心,官方建议按"核心数×2+磁盘并行数"估算,但实际应按并发与 DB 压测;minimumIdle 控制最小空闲连接;connectionTimeout 控制获取超时;connectionTestQuery 设置验证查询。

HikariCP 的哲学是"保持代码路径最短、最精简",每一项优化都直指减少获取/归还连接时的开销与锁竞争。maximumPoolSize 设太大反而不利于性能,需结合数据库并发实测。

#
★★

15. 连接复用时会话状态的残留(SET 变量、临时表、用户变量)对业务的影响与清理

连接复用时会话状态的残留(SET 变量、临时表、用户变量)对业务有什么影响?如何清理?

  • 会话级状态残留的成因
  • 对业务的影响(时区、schema、临时表污染)
  • 清理策略(DISCARD ALL、连接初始化 SQL)

连接池复用连接时,上一个业务留下的会话状态如果未清理,会污染下一个请求。例如:SET 的 time_zone、sql_mode、character_set 会影响后续 SQL 的行为;CREATE TEMP TABLE 的临时表在连接复用后仍存在,可能被下一个事务误用;MySQL 用户变量(@var)与 PostgreSQL 预编译语句、LISTEN/NOTIFY 等也会残留。影响表现为时区显示错误、脏数据、预编译语句冲突等。清理方式:MySQL 不存在 RESET CONNECTION 语句(仅 RESET 子句) 或连接池配置初始化 SQL,PostgreSQL 可执行 DISCARD ALL,或在获取连接时重置关键会话设置。

会话状态残留是连接复用的"副作用",必须通过"取出即重置"或"归还即清理"来保证连接的隔离性。DISCARD ALL/RESET CONNECTION 是标准清理手段。

-- PostgreSQL 清空会话级状态
DISCARD ALL;
-- MySQL 重置连接会话状态
RESET CONNECTION;
#
★★

16. 连接池与长事务的相互影响,长事务长时间占用池连接导致池耗尽,如何用事务超时、连接最长持有时间与慢 SQL 告警治理?

连接池与长事务如何相互影响?如何用事务超时、连接最长持有时间与慢 SQL 告警治理长事务导致的池耗尽?

  • 长事务占用池连接导致池耗尽的机制
  • 事务超时设置
  • 连接最长持有时间与慢 SQL 告警

长事务长时间持有连接,导致连接池中的连接被占用,池被耗尽,后续请求只能排队或超时失败。治理手段包括:设置事务超时(如 Spring 的 @Transactional(timeout=...) 或数据库 lock_wait_timeout、innodb_lock_wait_timeout),防止事务无限挂起;设置连接最长持有时间(连接池层面记录借用时间,超时告警);对慢 SQL 设置告警,及时发现长事务并定位;通过监控(如 MySQL 的 information_schema.innodb_trx 查看长事务)主动处理。同时避免在事务中做非必要的耗时的网络/外部调用。

长事务是"连接占用"与"事务时间"双重维度的资源问题,治理要"限时 + 可观测 + 定位"。事务超时是硬约束,慢 SQL 告警是软手段,两者结合才能根治。

#
★★

17. 坏连接剔除与故障转移,数据库重启/网络闪断后连接池如何检测并剔除失效连接(validation 查询、错误分类),避免雪崩?

数据库重启或网络闪断后,连接池如何检测并剔除失效连接?如何避免雪崩?

  • 失效连接检测手段(validation 查询、错误分类)
  • 剔除失效连接
  • 避免雪崩的治理

数据库重启或网络闪断后,池中的连接大部分已失效(socket 被关闭),但客户端不感知。连接池通过 validation 查询(如 test-on-borrow、test-while-idle)主动探测,发现失效连接即剔除并重建;同时按错误分类(如连接被重置、communication failure 等)判定连接不可用,标记为坏连接。为避免雪崩,关键是:合理设置连接校验超时与获取超时(connectionTimeout),避免大量请求同时重建连接压垮数据库;数据库重启后应有限度地重建连接(如熔断、指数退避),防止"连接风暴"导致数据库再次不可用。

雪崩的根源是"大量失效连接同时被发现并同时重建"产生的连接风暴。处理方法:先剔除失效连接,再限速重建(退避、限流),让数据库在恢复期平稳承接。

#
★★

18. 连接池的监控指标,活跃/空闲/等待连接数、获取等待时间与拒绝次数如何采集与告警,连接池容量与数据库 max_connections 的联动治理?

连接池的监控指标有哪些?如何采集与告警?连接池容量与数据库 max_connections 如何联动治理?

  • 关键监控指标(活跃/空闲/等待连接数、获取等待时间、拒绝次数)
  • 告警阈值
  • 池容量与 max_connections 联动

连接池核心监控指标包括:活跃连接数(activeCount)、空闲连接数(idleCount)、等待获取连接数(waitingThreadCount)、平均获取等待时间、获取连接超时/拒绝次数。这些指标通过连接池的 JMX 或内置监控(如 Druid Monitor、HikariCP Metrics)采集,并配置告警:如活跃连接持续接近 maxActive、等待线程数过高、获取超时次数增长时触发告警。联动治理上,池容量上限必须低于数据库 max_connections 并预留余量,当池满且等待增多时,既要考虑扩容池,也要排查是否存在长事务、连接泄漏或数据库连接紧张,避免盲目加大池导致数据库连接耗尽。

监控让"连接池是否健康"可量化。告警发现"池即将耗尽"的预警,联动治理则从"应用侧加大池"与"数据库侧 max_connections"两端平衡,防止把压力转移到数据库。

#
★★

19. 多数据源与读写分离下的连接池设计,主库/从库连接池独立配置的依据,动态数据源切换时连接与事务的绑定如何保证?

多数据源与读写分离下连接池如何设计?主库/从库池独立配置的依据是什么?动态数据源切换时连接与事务绑定如何保证?

  • 主从库池独立配置的依据
  • 动态数据源切换(AbstractRoutingDataSource)
  • 连接与事务的绑定

读写分离下主库与从库应使用独立的连接池,因为它们的负载与并发不同(写库连接数少但关键,读库连接数多),且各自 max_connections 与故障处理独立。动态数据源切换常用 AbstractRoutingDataSource(如 MyBatis 的 @DS、Spring 的 RoutingDataSource)按方法/注解路由到对应数据源。关键是连接与事务的绑定:事务一旦开启,连接必须绑定到事务并固定到同一数据源,切换数据源不能中途换库,否则事务的一致性无法保证。实现上常在事务开启时按当前路由 key 固定连接,并确保事务内所有操作走同一数据源。

路由发生在"取连接"时,事务一旦启动就锁定了连接与数据源,后续操作不能切换。这是读写分离与多数据源下保证 ACID 的关键。

#

20. 连接池连接的前置初始化(初始化 SQL)与字符集/时区设置

连接池连接的前置初始化(初始化 SQL)与字符集/时区设置是什么?

  • connectionInitSqls 初始化 SQL
  • 字符集与时区设置
  • 保证连接会话环境一致

连接池支持连接初始化 SQL,在连接建立时执行,用于统一设置会话环境。MySQL 常用 SET NAMES utf8mb4、SET time_zone、SET sql_mode 等,保证每个连接使用一致的字符集与时区,避免因连接初始化不一致导致乱码或时区错误。Druid 的 connectionInitSqls、HikariCP 的 connectionInitSql 都支持该配置;也可以在 JDBC URL 中通过 characterEncoding、serverTimezone、useUnicode 等参数指定。这样确保所有从池中取出的连接处于一致的初始化状态。

初始化 SQL 是"会话级统一配置"的手段,能避免每个连接各自的 SET 状态不一致带来的隐性 bug。字符集与时区是乱码和日期错乱的头号来源。

SET NAMES utf8mb4;
SET time_zone = '+08:00';
SET sql_mode = 'STRICT_TRANS_TABLES';