一顿改

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

背景在公司里面我负责的是积分商城一块,里面的积分商品也跟其它商品一样,超卖是绝对不可以的。。。。

刚接手到积分商城我刚来的时候,积分商城已经有了自家优惠券的功能,整个商城就2件商品:满5减1+满10减2.

我要做的第一个功能就是添加新的功能:第三方优惠券(其实就是跟我饿了么什么的一样啦)。自家的优惠券是通过模版生成的,给用户一个兑换码;第三方优惠券是从数据库里拿的(第三方给的),同样是给用户一个兑换码。

具体实现的思路就是:找到这个商品,然后通过这个商品的某个字段来找到对应的兑换码,这里的兑换码会有很多,那么给他一张就行了。发兑换码肯定是分为2步的:1.从库里面取一张,2.将取出的这一张状态标记为已使用。这里会出现一个问题,并发的情况下,也就是1.2步之间如果打个断点,那么2个人会得到相同的一张兑换码,而且第二次update的时候是对数据库没有影响的。

这里的解决方法就有很多了:

1.sql不怕累的:来个存储过程;

2.java不怕累的:根据update返回的结果是1还是0(有没有有效的update),来各种判断各种处理;

3.不怕慢的:把2个操作放在同一个事务里面,,其实我最早的想法就是这样的,因为以前做项目没啥效率要求,我也一直觉得这样很好。但是我们公司的读写是分离的,从代码里面就能看到读的slave是不带事务的,至于他们使用的是否为同一个数据库,还得看生产环境上怎么配。当时我就想把这里读的那个也换成master来,然后加个事务,这样基本就可以了。但是带我的那个非常不满意,表示如果把读加上了事务会非常的慢,所以被否定了。。。现在想想也是有些道理,这个能否实现还得看事务隔离级别的设置,而隔离级别要是满足了说不定运行起来还真的阻塞的哭了。。

4.最终的实现方案:我们最后用的是redis,所有的优惠券统一从redis中取,如果redis中没有,那就一次性从数据库中拿出5条来。使用了这个方法,让我对redis的了解多了不少。后面商品修改的时候,由于redis中的缓存也让我吃了不少苦---就是找不到问题所在,最后发现原来问题出在缓存没清。。。

小结第三方优惠券的处理方案:把优惠券放倒缓存中,然后统一从缓存中取。

后来商城的抢购活动第三方提供了10件衬衫,要求我们搞个抢购。

当时给的时间也不多,也没准备改啥代码,就插了10条数据到第三方兑换券的数据库,把衬衫当做第三方优惠券来看,然后最后哪10个人抢到了再从兑换记录里面找。

之前用redis处理了并发的问题,所以对于这个抢购还是比较自信的。。最终也是没有出现问题。

v3.0要求添加抽奖功能也就是后天要上线的新功能,这两天可把我给忙坏了;当时听到要添加抽奖功能的消息时,可把我给高兴坏了,可有好玩的东西写了。

当时脑袋里浮现出来的就是各种贵重物品抽抽抽,产品也说到时候抽iphone啥的。

这个可千万要小心,抽奖这事可别抽1个iphone搞出10个同时中奖来。。。

我的想法时这样的:

第三方券能够通过实实在在存在的券来控制,而其它的东西,没有实际存在的券,该怎么实现并发不出问题呢?

第一个想法还是用缓存,这次把商品的剩余数量给放到redis中来,把分布式的并发交给redis来处理,每次get and 减一 嘛。

这个功能redis里好像是有,但是在sPRing-data的redis封装里面我没找到对应的方法。

于是我就想去找找以前的自家优惠券是怎么写的,怎么控制不超买的,因为这个通过模版生成的东西也是通过商品的剩余数量来控制的,也并没有实际存在的券。

代码看完了,也没发现哪里控制了并发,更没发现哪里用到了缓存。

喜出望外,为啥?因为我发现了带我的人代码的问题,终于可以修复一下他的代码来展现一下自己了。

于是我去网上搜有关 抢购超卖 的处理方案,基本上就是

1.缓存,就是我上面说的

2.消息队列,应该就是序列化吧,别抢,都给我排队去

3.存储过程,我都懒得看。因为我知道我看明白了我也懒得写那么长的sql的

4.update table set a=a-1 where a>=1 and id =x ,行锁

这里我选择了第4中方法,因为带我的人说,不需要用缓存,使用sql就能完成,有行锁。

网上的说法是,请求量很大的时候行锁会使数据库压力非常大。我也觉得很有道理,不过想想我们积分商城的访问量,就先用这个简单的把,哪天访问量吃不消的,让我改成缓存我也乐意。

一顿改翻了一下原来的代码,发现并发是肯定会出问题的,为此我还测试了下,打了个断点,的确出问题了。

之前的sql是这样写的:

update table set a=a-1 where id =x

然后剩余数量这字段还是unsigned的,所以报了out of range的错,这明显就是要超卖,而数据库缺报错了,不过2个用户都提示兑换成功了。。

sql改了后,还是有问题,为啥,因为返回值没用上啊。

之前的剩余数量可以说就是用来标记一下还有多少个,用来平常用用的。

现在我把它提到了方法的第一行,判断返回值是否为0,多了一个处理并发的功能。

小结简单处理并发:

update table set a=a-1 where a>=1 and id =x

根据返回结果来看,如果为0,表示卖完了。不为0再继续下面的操作

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航