Tomcat启动失败、数据库连不上、连接池报ConnectionTimeoutException,主因是环境变量(JAVA_HOME、CATALINA_HOME)路径错误或连接池配置未对齐:JAVA_HOME须指向JDK根目录而非bin或JRE,CATALINA_HOME须为Tomcat解压顶层目录;数据库连接池需显式声明Resource并禁用Spring Boot自动配置,参数须按MySQL max_connections、Tomcat线程数反推,避免拍脑袋设置。

Tomcat 启动失败、数据库连不上、连接池报 ConnectionTimeoutException,八成是环境变量或连接池配置没对齐——不是代码问题,是容器底座没搭稳。
JAVA_HOME 和 CATALINA_HOME 必须指向正确路径
Tomcat 依赖 Java 运行,但不读 JAVA_HOME 就会 fallback 到系统 PATH 查找 java,容易错用 JRE 或旧版 JDK。而 CATALINA_HOME 错位会导致脚本加载错误的配置、日志目录甚至 classpath。
-
JAVA_HOME必须指向 JDK 安装根目录(如/usr/lib/jvm/java-17-openjdk-amd64),不能是bin子目录,也不能是 JRE -
CATALINA_HOME必须是 Tomcat 解压后的顶层目录(如/opt/tomcat),不能带/bin或/conf - Linux 下建议写入
/etc/profile.d/tomcat.sh,避免只对当前用户生效;Windows 下在“系统属性→环境变量”中设为系统变量 - 验证方式:执行
$CATALINA_HOME/bin/version.sh(Linux)或%CATALINA_HOME%\bin\version.bat(Windows),输出应含 JDK 版本和 Tomcat 版本
数据库连接池必须显式启用并禁用默认自动配置
Tomcat 默认不启用任何连接池,context.xml 里只配 url 和 driverClassName 是不够的;Spring Boot 等框架若开启自动配置,反而会绕过 Tomcat 的 DataSource,导致你调优的参数完全失效。
- 确认
context.xml中<Resource>声明包含type="javax.sql.DataSource"且factory明确指定(如org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory或 HikariCP 的com.zaxxer.hikari.HikariJNDIContext) - Spring Boot 项目需在
application.yml中关闭自动配置:spring.datasource.jndi-name: java:comp/env/jdbc/YourDB,并设置spring.datasource.initialization-mode: never - 若用 HikariCP,不要混用 DBCP 的
maxTotal参数——Hikari 对应的是maximumPoolSize,设错会静默忽略 - MySQL 8.0+ 驱动必须用
com.mysql.cj.jdbc.Driver,旧版com.mysql.jdbc.Driver在高并发下会卡死连接获取
连接池参数必须按 CPU 和数据库承受力反推,不能照搬文档
网上流传的 “maxTotal=100” 或 “minIdle=20” 是典型拍脑袋配置。真实瓶颈常出现在连接数与数据库最大连接限制、Tomcat 线程数、JDBC 驱动超时三者不匹配上。
-
maxTotal(DBCP2)或maximumPoolSize(Hikari)不能超过 MySQL 的max_connections值(可通过SHOW VARIABLES LIKE 'max_connections';查),建议设为该值的 70%~80% -
minIdle应 ≥minSpareThreads(Tomcat 线程池最小空闲线程数),否则请求一来就要等连接创建,放大响应延迟 -
connectionTimeout(连接池获取连接超时)必须 connectionTimeout(Connector 层),否则连接池还没报错,Connector 已先断开请求 - MySQL URL 必须加关键参数:
?useSSL=false&serverTimezone=UTC&connectTimeout=3000&socketTimeout=30000,缺任意一个都可能在负载突增时出现诡异挂起
日志里反复出现 AbandonedObjectPool 或 Connection leak
这不是连接池配置问题,是应用层没规范释放连接。Tomcat 连接池本身不检测业务逻辑是否 close,只靠归还动作判断;一旦某次 Connection.close() 被跳过(比如异常分支漏写),该连接就永远滞留在使用中,maxIdle 再大也救不了。
- 检查所有 DAO 层或 JDBC 模板调用,确保
try-with-resources包裹Connection/PreparedStatement,或手动finally { conn.close(); } - DBCP2 可启用
removeAbandonedOnBorrow=true+removeAbandonedTimeout=60强制回收,但这是兜底手段,会丢数据一致性,仅用于紧急止损 - HikariCP 默认不回收泄漏连接,必须配
leakDetectionThreshold=60000(单位毫秒),超时未归还会打 WARN 日志,但不会自动 close - 最可靠的方式:在测试环境开启 P6Spy 或 datasource-proxy,监控每条 SQL 对应的连接生命周期,定位哪段代码没归还
真正卡住系统的,往往不是最大连接数设太小,而是 minIdle 太低导致冷启动抖动、connectionTimeout 设太大掩盖了数据库响应恶化、或者 JAVA_HOME 指向了 OpenJDK 8 却跑着需要 JDK 17 的新驱动——这些细节不校验,调参就是沙上筑塔。

















