天道不一定酬所有勤
但是,天道只酬勤

一次数据库连接池满问题排查与解决

GitHub 19k Star 的Java工程师成神之路,不来了解一下吗!

之前一段时间,业务在线上经常出现频繁的数据库连接池满的报警,报错信息如下:

Caused by: ERR-CODE: [TDDL-4103][ERR_ATOM_CONNECTION_POOL_FULL] Pool of DB 'cn-zhxxx_i-xxx_fin_risk_xxx_30xx:33.10.xxx.xx:30xx' is full. Message from pool: wait millis 5000, active 10, maxActive 10. AppName:FIN_RISK_xxx_APP, Env:ONLINE, UnitName:null. 

然后去排查了一下SQL耗时,发现有大量的耗时SQL,并且执行耗时和锁耗时差不多是相等的。

然后看下这条具体的SQL语句:

UPDATE collection_case
SET gmt_modified = now(), lock_version = lock_version + ?, hands_count = ?, case_state = ?,  max_ovd_days = ?, case_class = ?, cur_ovd_principal = ?,collection_amount = ?
WHERE id = ?
  AND deleted = ?
  AND lock_version = ?

其实就是简单的一个更新语句,其中使用了乐观锁进行并发控制。

为什么乐观锁还会导致大量的锁耗时呢?

虽然乐观锁是不需要加锁的,通过CAS的方式进行无锁并发控制进行更新的。但是InnoDB的update语句是要加锁的。当并发冲突比较大,发生热点更新的时候,多个update语句就会排队获取锁。

而这个排队的过程就会占用数据库链接,一旦排队的事务比较多的时候,就会导致数据库连接被耗尽。

这类问题的解决思路有以下几个:

  • 1、基于缓存进行热点数据更新,如Redis。
  • 2、通过异步更新的方式,将高并发的更新削峰填谷掉。
  • 3、将热点数据进行拆分,分散到不同的库、不同的表中,减少并发冲突。
  • 4、合并更新请求,通过打批执行的方式来降低冲突。

其中,第2个和第4个方案,存在一定的延迟性,会把实时更新变更异步更新,存在一定的数据实时性问题。

第1个和第3个方案,实现起来成本比较高,但是相对来说更加完整一点。

根据我们的实际业务场景,我们选择了第4个方案,将更新操作进行合并,批量执行。

比如本来需要100个并发都给用户增加积分,那么改成10分钟更新一次,一次就把10分钟中内的所有需要增加的积分汇总之后一次更新用户积分表即可。

(全文完)

扫描二维码,关注作者微信公众号
赞(0)
如未加特殊说明,此网站文章均为原创,转载必须注明出处。HollisChuang's Blog » 一次数据库连接池满问题排查与解决
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

HollisChuang's Blog

联系我关于我