数据库连接池耗尽导致服务停摆问题排查

现象是登录服务器查看服务状态依旧存活,但是页面无法访问,接口也调不通。查看日志会有如下的报错:

### Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
; Lock wait timeout exceeded; try restarting transaction; nested exception is com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
2025-04-10 15:18:20.012 [http-nio-18301-exec-84] ERROR c.m.i.h.i.i.s.alarm.impl.AlarmRecordServiceImpl - reciveMessage--error
### Error updating database.  Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
### The error may exist in URL [jar:file:/usr/local/hil/hos-hil-ibss-itss-1.0.0.RELEASE.jar!/BOOT-INF/classes!/mapper/mysql/WarningMsgSaveMapper.xml]
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: update t_sys_warning_msg set warning_times = warning_times + 1                   where             rule_id = ?             and equip_no = ?             and name = ?             and warning_level = ?                          and status != 2             and is_deleted = 0

上面日志中的 Lock wait timeout exceeded 是关键信息,意思是 锁定等待超时

🔍 来看下背后的逻辑:
✅ 情况一:真正的锁等待超时
某个事务持有锁(如行锁),其他事务等待这个锁;
超过 InnoDB 的 innodb_lock_wait_timeout(默认50秒);
抛出 Lock wait timeout exceeded。

✅ 情况二:连接池连接耗尽,间接导致锁等待超时
比如:
连接池最多支持 30 个连接;
某些请求执行了长事务,没及时提交或释放连接;
新请求来了,等待连接池分配连接;
连接迟迟分配不到,但代码里执行了 SQL(或者进入了数据库事务),等待被打断;
最终导致事务未能正常提交或获取锁,抛出锁等待超时异常。
也就是说,表面上是锁超时,实质上是线程卡在连接池拿连接这一环节上。

恰好项目使用的数据库连接池是 Druid ,而我又打开了监控页面

可以看到执行中的线程数是50,而数据库连接池的线程数一共就50,所以判断是情况2。

如何解决这个问题

定位sql,优化这个sql是正确的做法,可以去Druid监控页面找执行时间最长的sql。
或者使用 show processlist 去查询 mysql 当前的线程状态,看看是哪个sql一直执行不完。

posted @ 2025-04-10 16:10  大唐冠军侯  阅读(217)  评论(0)    收藏  举报