php - 公司要给用户发50万个红包,怎样解决并发,以及发送超额的问题?
迷茫
迷茫 2017-04-11 09:41:13
0
6
790

1.是这样的,公司要求发送50万个红包给用户,并且限制了发送的总金额为50万,每个用户抢到的金额都是随机的,该如何保证用户在活动开始的时候,发送红包不会超额的问题。

我想了几种方案:
第一种:
当用户A点击了抽奖的按钮以后:发送一个AJAX请求,后台接收到了以后,锁表,判断金额是否超额,然后减金额,发红包。这个方案在瞬间几十万的请求下,服务器要挂掉。
第二种方案:
还是当用户A点击了抽奖的按钮以后:发送一个ajax请求,后台接收到以后,生成红包的金额,然后将用户的user_id 以及红包金额存到一张mysql的队列表中,然后开一个新的线程来处理队列中的数据,但是这个线程处理了队列的数据以后,怎么告诉用户红包抢到了的金额以后是否抢成功了。并且我也测试了下服务器每秒钟差不多只能处理2000多个mysql操作,但是如果请求每秒达到几万,那岂不是用户的每个请求要等待很久才能知道自己是否抢成功了?
第三种:
还是当用户A点击了抽奖的按钮以后:发送一个ajax请求,后台接收到以后,生成红包的金额,然后将用户的user_id 以及红包金额存到一张mysql的队列表中,存进去了以后,就直接调用一个方法来处理队列,但是这样的话,好像挺消耗资源的,感觉不是很合理。

求各位大神,能给小弟指点一下~

迷茫
迷茫

业精于勤,荒于嬉;行成于思,毁于随。

全部回复(6)
Ty80

我有个方案,不必使用数据库,性能毫无压力。
直接在内存中提前生成一个随机数组,长度50万,值的总和是50万元。过来的请求直接按顺序到数组中拿金额即可。前50万名能抢到红包。
当然访问数组要锁。用户和金额的对应要存好,先存放在内存中,再存到数据库中。

阿神

生成红包的动作都在活动开始前就生成好,用户参加活动,主要是一个“领取”的动作。
50万元,50万个红包,提前生成好,就是一个数组,全部放到redis队列里面。
好处:可以方便控制红包的数量,额度。
用户点击抽奖,ajax请求,然后后端代码从队列出队,就知道用户抽了多少了,后续的就是插入一条中奖流水,以及修改用户余额。
队列请求为空,直接返回红包已抢完。

Peter_Zhu

这个问题本质上就是和秒杀差不多,解决方法有N多,我这里就简单说一个大体思路,需求怎么做,其实怎么做都行,解决方法有太多太多。
优先统一的是:把绝大部分请求由缓存数据库挡住,至少保证不宕机。
那么谈谈具体实现:
1.从技术上做优化:优先完成用户体验的部分,如抢到红包,给个随机数,客户端直接帮用户的钱数装上(这个钱是假的,只是客户端直接把当前用户余额+随机数,实际上这个钱还是没有入库的,然后这个随机数入NoSQL队列,后端异步脚本不断运行,然后把钱真的加进去),你看支付宝集福也是这样,钱不是立刻到账的。
2.从需求上做优化:抢红包之后不直接显示价格,而是由另外一个页面负责展示价格。两个操作可以分为两个服务器,抢红包由NoSQL服务器直接抗,抢完不直接显示价格,而是在展示页面显示,展示页面又是另一台服务器(做一个分流),抢的过程中只是告知一个结果(是否有抢到),而有另外一个定时任务进程负责生成抢到的红包数量。
3.剩下的就是防外挂策略和红包分配策略了.
防外挂策略可以通过一些机制来处理:
同一来源、同一设备类型、相似UA、特定规则的IP,相同的某些字段等。利用神经算法的逻辑。假设某一条件,例如UA的特征字段,短时间内出现,就激励这一特征。同时使用代理,又激励另一特征。当这类特征的短时间内被激励到一定次数后,拦截符合这类特征的所有请求。
红包分配也是如此:第一是红包得相对公平,数量差距不能过大,这个算法/函数/类库 网上就很多了,直接找一个或者直接写一个满足你公司业务需求的即可。第二红包不是即时抢即计算随机随机的,而是提前计算好摆在队列里面的,那这里面可以做的文章就多了,如50万红包可以根据用户id分成50万个哈希桶,当用户来抢的时候我们就根据他的id判断它属于哪一个哈希桶里,这样他就等于是在一万大小的队列里面进行随机,并且当这个哈希桶的红包被分完了再跳到下一个哈希桶里即可。

剩下的强调一点,开源节流(增大可支持的连接数,并发数,减少用户直接访问数据库的次数),这是优化根本,至于找一个合适的方案,其实蛮简单的,想不想而已。

Ty80

使用redis redis性能高 50W个红包数量和50w个红包的总金额存放到redis中.每次用户发送ajax就数量减一decr num [每秒几万请求redis应该小菜一碟(如果单个redis不行还可以使用集群)] 服务层随机生成一个红包金额 同时操作redis中的数据减去相应的红包金额. 并且记录用户ID和金额(在一个事务中进行)redis中的操作 即set 用户ID 和 金额数.等用户抢完后将redis中的数据导入mysql数据库中 很好的避开了数据库的锁表问题 如果服务器此时访问量超过了服务器承受最大压力 拦截上游 在service层返回给用户 稍后再试

巴扎黑

这种高并发的坑我以前遇到过。。客户为了砍价隐瞒了需求,等抢红包那天服务器直接就拖垮了,现场太惨烈,哈哈。

多考虑下多并发的问题吧,不能单用普通的事务处理逻辑那样考虑。直接查询数据服务器这到时候肯定不行,得想法把能分离开的操作尽量分开。比如把一部分请求和数据读取放到NOSQL数据库里,什么都在MYSQL查询数据库压力太大了。

一定要在红包后台控制好限额。。或者账户存的钱别一下存太多,不然有BUG真就哭了。当初就遇到过有人用工具刷红包,看访问日志都是毫秒级的,唰唰的。。

总之谨慎点。坐等有经验的大神来指点。

大家讲道理

50w个红包,可以考虑采用mq+redis+mysql实现,每次用户的一个请求进来,就放到mq中,然后不断从mq中取出请求,在redis上判断是否有红包剩余;没有就直接返回未抢到;如果有,那么就到mqsql做剩余钱数的减法,具体怎么减,得你们自己根据策略来实现了,然后更新redis。楼上的回答大多数都是强依赖于redis的,如果redis挂了,那么整个服务就不用了。

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!