超链接网站建设/网站优化排名推广
在我的袋鼠玩趣城小程序项目中,用户领取优惠券的操作时,出现上数据重复记录的现象,也就是说本来只能每人领取一张的操作,个别用户领到了2张或者3张
分析了原因:可能是网络状态不好的情况下,用户多次点击了领取按钮,导至类似高并发的场景
解决办法:
1:从前端解决,(这个方法可以缓解一部分,但我个人认为不可能杜绝)
小程序的前端加上 防抖操作或者做节流操作
2:从后端解决,(php 后端)
方法又有三种
a: 使用php的文件锁
//这里为高并发的情况加上文件销$handle = fopen("./lock.mytxt", "w+");if (flock($handle, LOCK_EX)) {$result = $free_ticket_model->getFreeTicket($id, $store_id, $this->userInfo["uid"]);flock($handle, LOCK_UN);fclose($handle);if ($result["error"] == 0) {return JsonService::successful("ok");} else {return JsonService::fail($result["msg"]);}}
要记录完成操作后,解锁,并且释放资源
b: 使用mysql数据库的锁 类似 select * from table for update
$store_order_share = StoreOrderShare::where("order_id", $order_id)->lock()->find();
这是一个tp5中的加锁的方式
注意,这个写法必须是和 mysql的事务一起用才有效 也就是说上面的这句话必须是 Db::startTrans();
bb:说到数据库,还有一个方法可以解决这个重复写入的问题,那就是使用数据库的 索引 唯一索引,如果有重复的数据,数据库直接报错,就可以解愉了
c: 使用 redis来解决,使用 setnx 加上 setexpire watch 和redis的事务来解决
伪代码如下
public function testredis(){$redis = new \Redis();$redis->connect("127.0.0.1",6379);if($redis->setnx("lock",1)){$redis->expire("lock",5);$redis->watch("lock");$redis->multi();var_dump("做领券的操作");var_dump("领取完毕,删除这个key");$redis->del("lock");$redis->exec();}else{return JsonService::fail("领取失败");}}
使用setnx来加锁,并设置一个锁的自动过期时间,使用watch监视这个锁,使用事务使其成为一个原子性的操作
大至说一下为什么这么做吧
设置过期时间,是为了防止拿到锁的程序执行时中间出错,没有及时解锁,程序就成了一个死锁状态了,所以要让锁自动过期
为什么又要用事务来操作呢?
上面的锁是5秒过期,并且,程序在执行过无之后会删除锁, 如果其中有一个请求执行了8秒,那么在第5秒的时候,锁过期,另一个请求会抢到锁,开始执行,在第8秒时第一个请求会删除锁,请问它自己的锁已经过期了,此时它删除的锁会是谁的,所以使用了事务,来保证其原子性