数据库挂了导致的生产事故
那是一个休闲的周六上午,我吃着面,突然看到工作群信息两条告警信息:1.生产数据库异常。2.下单业务连接数据库失败......不是吧阿sir,这种事都能让我碰到,手中的面突然不香了。接着领导信息接踵而来,我说赶紧先让运维重启数据库服务器吧,我也没权限操作数据库服务器呀。说完大量吸入手中的拌面,赶紧回家看看问题。
业务背景

数据库主从类似keepalived的机制,主从都安装keepalived,keepalived会定期检测心跳,主机器挂了会进行vip飘移。简单来说就是访问43VIP,就是访问41,检测到41挂了,就会进行VIP漂移和数据库主从切换。一开始是说vip没飘过去,我就说赶紧重启41。。不然没法玩,后面说已经飘过去了,43是能访问的,但是程序还是有问题。我还以为是43访问不了,那就和程序没有关系,原来还是有关系的....。然后运维重启了在线系统,问题解决。

问题定位
因为运维重启前没有打印线程快照,所以只能通过业务日志的时间去定位问题.....
首先记录几个时间:
故障发生时间 11:36:28 - 11:54:30
最后一次HTTP请求返回:11:37:13
路径:tomcat的logs目录下localhost_access_log.2023-03-11.txt
第一次系统报错时间:11:37:21
路径:catalina.out
确定在36分半的时候,数据库已经挂了,在线系统的请求全卡在请求数据库那里,然后坚持了差不多一分钟就无法处理请求了。系统卡了20分钟,重启后恢复。
故障分析
为什么获取数据库连接失败没有抛异常呢?
因为c3p0连接池的5秒超时被注释掉了。。为什么注释掉了呢,这要追溯到上一次的生产事故了...大概就是某一天的并发很高,然后系统网络又有点卡顿,导致数据库连接数满了,有一些连接请求数据库5秒后就抛异常了,最关键的是..系统try catch把异常吞了,导致没有触发重试机制。然后领导就觉得让他慢慢消费也可以,就注释掉了。这其实算是一个策略,数据库连接池满了到底怎么处理,如果是短时间内的堵塞,可以让他慢慢消费,如果是长时间的堵塞,就只能抛异常了。就要从结果来说还是要设置超时时间比较好,就不会导致请求堵塞。。
为什么会报C3P0的死锁?
网上搜c3p0 APPARENT DEADLOCK发现一大堆案例。我以为是因为这个死锁导致的系统崩溃,结果在本地环境能复现这个问题。我是本地搭了一个数据库,然后没有启动服务,连接池连不上就会报这个错误。

接着我尝试访问下单接口,看看多久返回
2分钟。。直接卡死
如果设置了超时时间
接着我再把本地数据库打开,系统正常访问,所以问题是和c3p0死锁没有关系,就是请求全堵塞了。
优化方案
- 设置超时时间5秒,虽然可能会抛弃一部分请求,但是不至于堵塞整个系统
- 业务拆分,把关键业务和非关键业务分开部署,现在是由于一个上报接口并发太高导致tomcat连接数和数据库连接数都用完了,后期还能根据业务拆分数据库。
- 加大tomcat连接和c3p0数据库连接数,现在tomcat默认200的连接数,c3p0最大设置200,要把这两个参数翻倍,让系统再坚挺一会儿。
- 数据库主从切换时间要更迅速一点,一分多钟才切换成功有点久了
- 告警出现值班运维要马上查看系统cpu和内存使用情况,如果出现异常需要记录下来,并打印线程快照,最后重启服务。
总结
由于主库挂了导致4台集群无法工作,就是因为一个超时时间的问题,也确实不应该。只能用要多想想一些特殊的情况,比如数据库挂了怎么办,线程池满了怎么办,http请求超时怎么办。一个大事故的发生都是多个小问题引起的。另外微服务思想也是重要的,不能因为某个小业务并发高导致系统堵塞。
转载自:https://juejin.cn/post/7210584884321927205