我服务端是采用Workerman+GatewayWorker+TP5
在使用中,如果并发不高的话,基本没问题,
但是一但有高并发时,同时需要插入或更新批量数据时,有时会出现锁表或者违反唯一约束了,
我已经在插入时先判断是否存在记录了,不存在才整合起来,一起插入的。
$remindData=[];//提醒库
//评论
$commentData=[];
$commentUserList=isset($objectItem['commentUserList'])?$objectItem['commentUserList']:[];//文章和相关评论和点赞的数据
if($commentUserList){
$commentUserList=array_column($commentUserList,null,'commentId');
$commentIds=array_column($commentUserList,'commentId');
$commentList=MomentsComment::where('object_id="'.$object_id.'"')
->where('commentId','in',$commentIds)
->select();
$commentList=collection($commentList)->toArray();
$commentList=array_column($commentList,null,'commentId');
foreach ($commentUserList as $ck=>$cv){
if(!isset($commentList[$cv['commentId']])){
$commentData[]=[
'object_id'=>$object_id,
'userid'=>$cv['username'],
'commentFlag'=>$cv['commentFlag'],
'commentId'=>$cv['commentId'],
'replyCommentId'=>$cv['replyCommentId'],
'reply_userid'=>$cv['replyUsername'],
'source'=>$cv['source'],
'type'=>$cv['type'],
'content'=>$cv['content'],
'createTime'=>$cv['createTime']
];
if($robot_userid==$friends_userid){
$wx_key=$robot_userid.'##'.$cv['username'];
$reply_wx_key=($cv['replyUsername']!=''?$robot_wxid.'##'.$cv['replyUsername']:'');
$remindData[]=[
'object_id'=>$object_id,
'wx_key'=>$wx_key,
'robot_userid'=>$robot_userid,
'friends_userid'=>$cv['username'],
'typ'=>($cv['replyCommentId']!=0?2:1),
'commentId'=>$cv['commentId'],
'replyCommentId'=>$cv['replyCommentId'],
'reply_wx_key'=>$reply_wx_key,
'reply_userid'=>$cv['replyUsername'],
'content'=>$cv['content'],
'remind_time'=>$cv['createTime']
];
}
}
}
}
//点赞
$likeData=[];
$likeUserList=isset($objectItem['likeUserList'])?$objectItem['likeUserList']:[];
if($likeUserList){
$likeUserList=array_column($likeUserList,null,'like_userid');
$LikeUserids=array_column($likeUserList,'like_userid');
$LikeList=MomentsLike::where('object_id="'.$object_id.'"')
->where('like_userid','in',$LikeUserids)
->select();
$LikeList=collection($LikeList)->toArray();
$LikeList=array_column($LikeList,null,'like_userid');
foreach ($likeUserList as $ik=>$iv){
if(!isset($LikeList[$iv['username']])){
$likeData[]=[
'object_id'=>$object_id,
'like_userid'=>$iv['username'],
'like_time'=>$iv['createTime']
];
//判断如果此文章是自已的,则入提醒库
if($robot_userid==$friends_userid){
$wx_key=$robot_userid.'##'.$iv['username'];
$remindData[]=[
'object_id'=>$object_id,
'wx_key'=>$wx_key,
'robot_userid'=>$robot_userid,
'friends_userid'=>$iv['username'],
'remind_time'=>$iv['createTime'],
];
}
}
}
}
$saveData=[
'object_id'=>$object_id,
'userid'=>$friends_userid,
'commentCount'=>$objectItem['commentCount'],
'createTime'=>$objectItem['createTime'],
'likeCount'=>$objectItem['likeCount'],
'nickname'=>$objectItem['nickname'],
'conents'=>$objectItem['contentDesc'],//文字内容
];
$result=false;
$snsItem=Moments::where('object_id="'.$object_id.'"')->find();//这是文章表
if(!$snsItem){
$wm=new Moments();
$result=$wm->save($saveData);
}
$execArr=[];
if($commentData){
$wmc=new MomentsComment();//这是评论
$wmc->saveAll($commentData);
}
if($likeData){
$wml=new MomentsLike();//这是点赞
$wml->saveAll($likeData);
}
$is_remind=false;
if($remindData){
$wmr=new MomentsRemind();//这是如果是自已发的,则加入提醒
$wmr->saveAll($remindData);
$is_remind=true;
}
之前也有使用INSERT IGNORE INTO来插入,不过也不是理想,想问下,像这种的在高并发下时要如何避免出现锁表或唯一约束呢。
最好队列处理 这种插入和修改 用队列,要不然没啥好办法
我已在onWorkerStart里定义了队列
$redisQueue->subscribe('CommentNotify',function($message)use($redis,$bWorker){
Common::CommentNotify($message,$redis,$bWorker);
});
然后在OnMessage里将收到的信息加入队列里。
$redisQueue->send(‘CommentNotify’,$data);
但是由于是多进程,所以当并发时,就会有多个进程同时在处理,这就会导致锁表或出现主建唯一冲突问题。
不知还有其它方案否。
laravel的orm有个upsert很方便
是不是代码写的有问题?
可以考虑增加一个写锁,让插入按顺序进行(有锁的情况下间隔x秒重新发布队列)。
或者给插入记录生成一个主健id, 比如使用