为了账号安全,请及时绑定邮箱和手机立即绑定

跪求!PHP怎么做到多线程的情况下不重复提交,不重复插入数据?

跪求!PHP怎么做到多线程的情况下不重复提交,不重复插入数据?

慕婉清6462132 2019-06-13 10:19:49
业务逻辑:用户进行投票,投票之后写入记录;投票成功后更改用户状态,不得再投票。直接通过postman测试接口是没问题的,数据都正常。但是只要通过多进程脚本运行测试的话,写入记录会增多。目前想到的解决方案:使用redis有序集合,使用时间戳毫秒写入获取第一个再做对比。简单来说,就是在毫秒维度进行做并发处理,但是感觉如果更高并发的话,应该也会出问题。使用队列服务处理。投票代码:$uid=Token::getCurrentUid();$codeNum=Token::getCurrentTokenVar('codeNum');//并发处理$redis=Redis::getRedisConn();$key=RedisKeyNameLibrary::USER_VOTE.$codeNum;$score=array_sum(explode('',microtime()));$value=build_rand_str(32).':'.$uid;$redis->zRemRangeByScore($key,0,time()-1);//清除1秒前的集合$redis->zAdd($key,$score,$value);$zRangeArr=$redis->zRange($key,0,-1);if($zRangeArr[0]<>$value)returnreturnError('提交失敗,請重新提交!',30002);$tran=$this->db();$tran->startTrans();try{//根据ID获取对应模型$model=self::get($uid);$data=array_merge($model->toArray(),$data);//验证$validate=newCodeValidate();$result=$validate->check($data,[],'vote');if(!$result)returnreturnError($validate->getError(),30001);$errorMsg=returnError('提交失敗,請重新提交!',30002);//更改为已投票$status=$model->data($data)->allowField(true)->save(['status'=>self::STATUS_1]);//保存成功后追加投票记录if($status!==false){$saveData=[];$models=User::all(array_map('intval',$data['user_ids']))->all();foreach($modelsas$k=>$v)array_push($saveData,['code_num'=>$data['code_num'],'uid'=>$v->data['id'],'name'=>$v->data['name'],'group_id'=>$v->data['group_id']]);$model->logs()->saveAll($saveData);$tran->commit();returnreturnSuccess();}return$errorMsg;}catch(Exception$e){$tran->rollback();return$errorMsg;}以下是多进程测试投票接口的脚本:for($i=0;$i<6;$i++){$pid=pcntl_fork();if($pid==-1){die("couldnotfork");}elseif($pid){echo"I'mtheParent$i\n";}else{$token='123123';$url='http://api.com/user';$query='user_ids[]=1&user_ids[]=39&user_ids[]=19&user_ids[]=30';$command='curl-H"token:'.$token.'"-XPOST-d"'.$query.'"'.$url;//子进程处理$res=system($command);file_put_contents($i.'_work.log',var_export($res,true));exit();//一定要注意退出子进程,否则pcntl_fork()会被子进程再fork,带来处理上的影响。}}//等待子进程执行结束while(pcntl_waitpid(0,$status)!=-1){$status=pcntl_wexitstatus($status);echo"Child$statuscompleted\n";}想问问,除了以上解决方案之外,还有没有别的解决方案?(最好不需要启动别的服务之类的)
查看完整描述

2 回答

?
回首忆惘然

TA贡献1847条经验 获得超11个赞

<?php session_start();$id_lucky = $_POST['id_lucky'];$customer = $_POST['customer'];$prize = $_POST['prize'];$hide = $_POST['hidden'];//var_dump($_SESSION);if($hide==$_SESSION['conn'])//你这里判断的session是什么时候存入session的{    $sql=mysql_query("insert into "._DB_PREFIX_."luckdraw (customer,prize) values ('$customer','$prize')");    //你这里没有判断 你应该在这里判断一下mysql_query是否执行成功 执行sql成功 是提交成功 否则是失败 建议你使用var_dump($sql)打印$sql看看$sql是什么数据类型    if($sql){    echo "亲,提交成功了哦";    }else{    echo '失败';    }       }else{    echo "<script>return false;</script>";    }session_destroy();


查看完整回答
反对 回复 2019-06-13
?
叮当猫咪

TA贡献1776条经验 获得超12个赞

//假设用户id
$userId=xxxxx;
$voteKey="votes:xxx:users:{$userId}";
//返回0,代表当前用户已经投票过了
if(0==Redis::hset($voteKey,'id',$userId)){
exit('你已经投过票了');
}
try{
}catch(\Exception$e){
//把当前用户踢出,给他继续投票
Redis::del($voteKey);
}
                            
查看完整回答
反对 回复 2019-06-13
  • 2 回答
  • 0 关注
  • 712 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号