Redis的妙用:我是怎么解决问卷配额问题的

分类:
JAVA
标签:
缓存
Redis
工作
作者:
何鑫
创作时间:
2019/12/15 19:43:56

摘要:用Redis扛住高并发的配额扣减

问题描述

对于问卷系统,配额管理是其中非常重要的功能。要谈论配额问题,我们需要首先需要了解什么是配额。举个例子来说,对于一份问卷来说,如果我们设定了题目:您的性别?我们要求选择男性的需要500份,女性300份,那么这个男500和女300便是这份问卷的配额,配额可以让我们更精确的控制我们的目标调查群体,同时节省调查成本。目前主流问卷网站都有提供配额管理的功能。

在高并发环境下,配额精准性是一大难点 。我们需要尽力避免配额不足或配额超出的问题,特别是配额超出,对于一份有奖励的问卷,配额超出也就意味着问卷调查成本的提升,在总量一定的情况下,也会同时引发部分配额的不足和部分配额的超出。

问题探索

我们有哪些方案来解决这个问题呢?

我们能想到的最简单的方案就是把配额数量记录于数据库,当问卷提交时查询配额数量,如果数量大于0,则进行扣减,否则认定配额已满,跳为废卷。我们仔细想一下,这个方案很简单,逻辑上也是合理的,但是真的没问题吗?我们设想一种情况,还是以上面的性别配额为例,假如男性已经收了499份,用户A答卷选择了男性,提交答卷时我们查询到还有1份,也就是配额未满,他可以正常提交答卷,但是如果此时B用户也选择了男性,他去查询配额发现也收了499份(A尚未更新答卷配额),B答卷就会认为自己也是合格的,好,现在轮到A扣减配额了,配额变成了0,B扣减配额,配额此时确变成了-1,我们实际收了501份答卷,配额超出了!

问题出现的环节我们很容易就可以定位到,在用户A未操作完成(查询和扣减动作)时,B就进行了操作,从而影响了结果。知道原因,我们自然也可以很容易的想出解决方案:只要A操作时,不允许B操作不就可以了!怎么做呢?我们可以在代码中锁住这段操作不就行了(java可以使用synchronized关键字),这样的话,同一时间只会有一个用户去操作配额,不就可以保证配额的正确性了么?这们想是没错,但是我们再想想,这种操作对于分布式系统合用吗?在分布式系统中,同样的代码可能部署在不同服务器上,普通的代码锁似乎并不能起作用,它只能锁住一台机器上的用户。

我们继续想下去,既然锁代码不适用,我们也可以锁数据库啊!把悲观锁使用上:

select 数量 from 配额表 for update

再执行更新,不就妥了!但是我们知道悲观锁锁开销较大,我们能不能使用乐观锁呢?安排上:

update 配额表 set 数量 = 数量 - 1 where 数量 >= 1

这样就比较理想了,我们判断是否有更新行数就行了,有更新就代表扣减成功,没更新就代表扣减失败,配额可能超出!

到此为止了么?当然不,前面的所有操作都是在数据库进行的,数据库也是有瓶颈的,在高并发环境下,数据更新频繁,锁竞争激烈,会给数据库造成很大负担,严重的会导致数据库崩溃!

那我们怎么办?轮到Redis出马了,Redis就是出了名的快,而且单线程决定它不会存在线程安全问题!Redis为我们提供了decr命令,它的作用是将key中存储的数据减去1,并返回减去后的值,我们只需要预先存入配额数量至Redis,在符合配额的情况下用desc扣减对应key的值,返回的值如果大于等于0即代表配额扣减成功!

我们经过一步步探索,最终找到了解决配额问题的最终方案,Redis就是我们应对高并发的神器!

问题总结

从这个问题中我们学到了Redis解决配额问题的妙招,它不仅仅运用于配额,只要是涉及库存扣减的问题,都有Redis登场的机会!我们也明确了解决问题的思路,遇到问题不要怕,针对问题的出现点,一步一步去解决,并学会从解决方案中发现不足,继续进步,重要的也不是问题最终的答案,而是寻找答案的过程,在这个过程我们一定会受益良多。

我们的问题结束了么?其实并没有完全解决,它还是有瑕疵的,发现问题的同学不妨在评论区告诉我你的答案,我们将在后续继续探讨,本文先到这里,下次见!

发表评论

温馨提示: 评论先审核后发布, 请勿发表不良言论

所有评论