先说一下我们目前情况:
我们在做一个活动平台,里面有很多活动可以上线,有很多活动可以抽奖,这些活动的抽奖与很多设定条件有关(这也是导致需要很多查询的原因)。目前服务端使用NodeJs
,数据库使用MongoDB
,缓存使用Redis
。
服务器配置:
阿里云服务器1台,4核8G;MongoDB为阿里云数据库8核16G(连接数5000);阿里云Redis为4G。
前提条件:
- 最早的时候抽奖没有使用队列,导致多人同时抽奖的时候,数据库响应不及时,奖品导致超发。
- 为了改正问题1,我们引入队列的形式进行抽奖,使用了一个队列,抽完一个,更新数据库的奖品使用量,然后继续抽下一个,导致瞬间只能抽奖一个人,问题是比如有100个人抽奖,也只能一个一个排着来,此做法解决了奖品超发问题,但是导致100个人同时抽奖,一个一个抽,最后一个出结果可能是几秒后了。
在问题2中,由于活动设定的抽奖条件很复杂,比如有这些设定:
- 活动最多可抽10次(不含通过其他渠道增加的次数),每天不超过3次
- 可以分享和邀约增加抽奖次数
- 判断是否在黑白名单内
- 判断是否在可抽奖,可中奖时间段内
- 判断当前抽奖人是否在某一次指定的次数内可中奖,比如第100个人必中一条毛巾
- 其他更多的限制……
遇到的问题:
- 在某个时间段多人抽奖时,MongoDB数据库CPU直接跑满100%占用,但是MongoDB内存,服务器内存和CPU都只有30-50%左右,丝毫不受影响。数据库CPU跑满导致接口响应异常慢,正常情况一秒内返回,跑满的时候直接卡死,接口10秒都无法响应。
- 由于
前提条件的第3点
,我们在一个用户抽奖过程中,进行了很多次数据库的查询,以判断他能否中奖,能中哪个奖,大概有10次数据库查询,然后执行抽奖,再下一个……。
问题分析:
我们尝试了多种办法以及压力测试,目前还没有很好的解决办法,还无法得知本质的问题是什么,是否如下:
- 由于很多人同时抽奖,每个人抽奖都产生多次查询和统计,导致MongoDB的CPU占用高
- 由于抽奖队列影响,比如排队一个一个抽,前面的人堵着,导致后面的人抽奖被堵着,从而导致接口响应变慢。
- 从阿里云数据库统计,从没有慢查询,即单次数据库操作超过100ms的记录。
- 我们各个数据表目前量也不大,数据量都在10万条以内。
- 服务器为弹性带宽10M,最高峰也就几M的带宽使用量,所以不是带宽问题。
- 服务器内存,CPU,负载指标等一切正常,目前所有问题都指向MongoDB的CPU占用率,因为MongoDB的其他指标如内存占用等一切正常,Redis指标也一切正常。
可能的解决办法:
将一些用户数据保存到
Redis
,不去统计数据库,比如用户抽奖前需要判断的总抽奖次数,今天抽奖次数,今天分享和邀约数等,放到一个Redis
的Hash
,假设如下:// redis中的用户统计信息 { // 用户今天分享量和抽奖次数,直接读取redis,不从MongoDB统计 todayShareTime: 5, todayLuckDraw: 2 ...... }
目前上面这个方案有尝试使用,但是对抽奖时MongoDB的CPU占用高没有显著解决,目前看作用不大,当然也有可能是没有将所有查询所需的数据放Redis的原因,因为那样子改动非常大,一时半会处理不了。
疑问
- 基于我们现在出现MongoDB CPU占用100%的情况,核心的问题在哪呢?
- 网上有非常多的抽奖服务,比如商城的秒杀,别人也会有有很多判断,查询,他们是怎么优化的呢?
- 我们使用队列一个一个去抽奖,这个思路是否正确,是否有其他更好,更优的做法去解决奖品超发的问题?
- 我们现在单次参与的人,预计也就几百个人抽奖数据库就扛不住了,没有所谓几万,几十万的流量。
以上是我们遇到的问题,哪位同学有遇到过,或者有对应的解决思路,或者方案,恳请指导一下,感谢!
我猜问题的本质是一次抽奖要走20~40次数据查询, 每次都算20ms好了, 走完一个人的流程也要600ms左右, 加上队列不能并发, 20人就要等10秒了.
简单的优化思路