概述
在昨天下午,在做业务系统每日巡检的时候,发现营销系统的日志里,出现了209次ERROR
,内容如下:
获取商品详情优惠券出错,request:{“productId”:123456,“shopId”:123456,“userId”:123456}
获取商品详情优惠券出错,request:{“productId”:123456,“shopId”:123456,“userId”:123456}
获取商品详情优惠券出错,request:{“productId”:123456,“shopId”:123456,“userId”:123456}
由于是用grep
命令找出来,具体是什么报错原因,不清楚,因此用了之前文章shell查找文件显示行号和对应区间的内容里提到的技巧,找出报错的详细内容。
Cause: org.springframework.jdbc.CannotGetJdbcConnectionException,Connection is not available
从日志上看,是获取不到数据库连接了,但是比较奇怪,获取商品详情优惠券这个接口我是加了缓存的,详情请看缓存时间小技巧-随机打散,理论上不太可能有很多的请求穿透到数据库才对呀。因此仔细分析了发生报错的时间和报错的请求入参,发现报错时间集中在中午12点01分这一分钟里,且大部分入参都是同一个shopId
的。至于如何用awk
统计shopId
,请参考awk神器呀
由于店铺的商品详情是按照店铺id
作为key
来缓存的,一旦某个时间段,店铺的并发请求量一大,而刚好缓存又失效了,如果没有做热key
防护,瞬间就有很多请求打到数据库了,造成数据库连接不够用。
解决方案
由于是以店铺id
作为key
的,只要这个店铺的用户进入到商详,就会调用获取商详优惠券的接口。这种key
属于大key
,如果我们缓存的key
加上userId
的话,那就没问题,因为按照userId
打散了。不过这样做有个问题,就是缓存的key
非常非常多,不建议这么干。
我这边是这么做的:
第一:增加营销系统的数据库连接数,之前才配置了几十个,对于这种C端应用,确实配置少了。
第二:加一个本地缓存,并针对本地缓存,做一个进程级别的本地锁,同一个key
并发过来查询,如果从缓存获取不到数据,则放一个请求过去,其他请求继续等待。