在高并发下老是出现锁表或违反唯一约束

freely

问题描述

我服务端是采用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来插入,不过也不是理想,想问下,像这种的在高并发下时要如何避免出现锁表或唯一约束呢。

889 2 1
2个回答

tanhongbin

最好队列处理 这种插入和修改 用队列,要不然没啥好办法

  • freely 2023-03-14

    我已在onWorkerStart里定义了队列
    $redisQueue->subscribe('CommentNotify',function($message)use($redis,$bWorker){
    Common::CommentNotify($message,$redis,$bWorker);
    });
    然后在OnMessage里将收到的信息加入队列里。
    $redisQueue->send(‘CommentNotify’,$data);
    但是由于是多进程,所以当并发时,就会有多个进程同时在处理,这就会导致锁表或出现主建唯一冲突问题。
    不知还有其它方案否。

  • tingfeng 2023-03-14

    laravel的orm有个upsert很方便

  • PHP甩JAVA一条街 2023-03-14

    是不是代码写的有问题?

小吴大大

可以考虑增加一个写锁,让插入按顺序进行(有锁的情况下间隔x秒重新发布队列)。
或者给插入记录生成一个主健id, 比如使用

$redis->incr('you table max id');
  • 暂无评论
年代过于久远,无法发表回答
🔝