【PHP】PHP代码处理(普通/不重要的)并发情况,例如pv统计(不使用MySQL行或表锁、避免程序冗余)
1、PHP代码处理(普通/不重要的)并发情况,例如统计pv数据:什么意思呢?
比如pv统计,某时间段pv数据不存在则新增,存在则更新+1,这时候会存在一个问题:我们查询的时候可能没有记录,但是准备插入的时候却已经有数据了(别的进程捷足先登)
解决办法:我们可以在MySQL设置 唯一主键,配合php代码进行控制,使用 try catch 捕获MySQL的异常,匹配异常代码,将重复的一行进行更新
/** * 页面pv统计 */ public function statPv(Request $request) { $node_id = $request->node_id ?? config('cache.master_node_id'); $openId = $request->input('open_id'); $event = Input::get('event') ?? ''; $route = Input::get('route') ?? ''; $data = S::show($route); if($event != 'show'){ return S::jsonReturn( ['info'=>'fail~~'] ); } $sessionKey = $request->input('3rd_session'); if(empty($openId) || empty($sessionKey)){ return S::jsonReturn( ['info'=>'fail'] ); } $userInfo = unserialize(WeChatDao::baseRedis('get', ':' . $openId . '_cache')); $sysSessionKey = unserialize(WeChatDao::baseRedis('get', ':key:' . md5($sessionKey))); if(empty($sysSessionKey)){ return S::jsonReturn( ['info'=>'fail~'] ); } if(empty($userInfo)){ return S::jsonReturn( ['info'=>'fail!'] ); } if ($userInfo['open_id'] != $sysSessionKey['openid']) { return S::jsonReturn( ['info'=>'fail!!'] ); } $url = $data['path']; $query = $data['query']; $trans_date = date('Y-m-d'); $trans_hour = date('H') . ':00'; $pvObj = StatPageVisitDay::where('node_id',$node_id)->where('trans_date',$trans_date)->where('trans_hour',$trans_hour)->where('url',$url)->first(); if(!empty($pvObj)) { $pvObj->increment('pv'); } else { $pvObj = new statPageVisitDay(); $pvObj->node_id = $node_id; $pvObj->trans_date = $trans_date; $pvObj->trans_hour = $trans_hour; $pvObj->pv = 1; $pvObj->url = $url; $pvObj->param = $query; try { $pvObj->save(); } catch (\Exception $e) { $message = $e->getMessage(); if (strstr($message, "Duplicate") && strstr($message, "for key ")) { log_write('插入pv表主键或唯一索引重复,再次执行更新操作' . $message); StatPageVisitDay::where('node_id',$node_id)->where('trans_date',$trans_date)->where('trans_hour',$trans_hour)->where('url',$url)->increment('pv'); } else { log_write("PV入库异常,原因{$e->getMessage()}"); MailDao::warning("pv统计异常. 页面uri={$route},", json_encode(['err_msg'=>$e->getMessage()])); return S::jsonReturn( ['info'=>'statpv insert failed'] ); } } } return S::jsonReturn( ['info'=>'success'] ); }