>  기사  >  백엔드 개발  >  PHP는 높은 동시성을 어떻게 해결합니까?

PHP는 높은 동시성을 어떻게 해결합니까?

王林
王林원래의
2019-09-19 17:57:084110검색

PHP는 높은 동시성을 어떻게 해결합니까?

PHP는 높은 동시성 문제를 해결합니다

여러 스레드가 동일한 파일에 쓸 때 "스레드 안전" 문제가 발생한다는 것을 알고 있습니다. 각 실행 결과는 단일 스레드 작업의 결과와 동일합니다. 결과는 예상과 동일하므로 스레드로부터 안전합니다. MySQL 데이터베이스인 경우 자체 잠금 메커니즘을 사용하여 문제를 해결할 수 있습니다. 그러나 대규모 동시성 시나리오에서는 MySQL이 권장되지 않습니다.

플래시세일 및 러시세일 시나리오에는 또 다른 문제가 있는데, 바로 '과잉 전송'입니다. 이 부분을 주의 깊게 관리하지 않으면 과도한 전송이 발생하게 됩니다. 또한 일부 전자상거래 회사에서는 구매자가 사진을 성공적으로 촬영한 후에도 판매자가 주문이 유효한 것으로 인식하지 못하고 상품 배송을 거부한다는 소식을 들었습니다. 여기서 문제는 반드시 가맹점이 배신적이라는 것이 아니라, 시스템의 기술적 수준에서 과잉 발행의 위험이 발생한다는 점일 수 있습니다.

1. 비관적 잠금 아이디어

스레드 안전성을 해결하기 위한 많은 아이디어가 있는데, "비관적 잠금" 방향에서 논의를 시작할 수 있습니다.

비관적 잠금, 즉 데이터 수정 시 잠금 상태를 채택하여 외부 요청에서 수정 사항을 제외합니다. 잠긴 상태가 발생하면 기다려야 합니다.

PHP는 높은 동시성을 어떻게 해결합니까?

위의 솔루션이 스레드 안전성 문제를 해결하더라도 우리의 시나리오는 "높은 동시성"이라는 점을 잊지 마세요. 즉, 그러한 수정 요청이 많이 있을 것이며 각 요청은 "잠금"을 기다려야 합니다. 일부 스레드는 이 "잠금"을 잡을 기회가 전혀 없을 수 있으며 그러한 요청은 거기에서 종료됩니다.

동시에 그러한 요청이 많아지면 시스템의 평균 응답 시간이 즉시 증가하게 되며 결과적으로 사용 가능한 연결 수가 소진되고 시스템이 예외에 빠지게 됩니다.

2. FIFO 큐 아이디어

좋습니다. 그러면 위의 시나리오를 약간 수정하여 요청을 큐에 직접 넣고 FIFO(First In First Out)를 사용합니다. 일부 요청이 잠금을 획득하지 못하게 되는 것은 아닙니다. 이렇게 보니 멀티스레딩을 강제로 싱글스레딩으로 바꾸는 듯한 느낌이 드시나요?

PHP는 높은 동시성을 어떻게 해결합니까?

이제 잠금 문제가 해결되었으며 모든 요청은 "선입 선출" 대기열에서 처리됩니다. 그러면 새로운 문제가 발생합니다. 동시성이 높은 시나리오에서는 요청이 많기 때문에 큐 메모리가 순간적으로 "폭발"되어 시스템이 비정상적인 상태에 빠질 수 있습니다.

또는 대용량 메모리 큐를 설계하는 것도 해결책입니다. 하지만 시스템이 큐에 요청을 처리하는 속도는 큐에 미친듯이 쏟아지는 요청 수와 비교할 수 없습니다. 즉, 대기열에 있는 요청 수가 점점 더 많이 누적되고 결국 웹 시스템의 평균 응답 시간은 여전히 ​​크게 떨어지며 시스템은 여전히 ​​예외 상태에 빠지게 됩니다.

3. 파일 잠금 아이디어

일일 IP가 높지 않거나 동시성 수가 그리 많지 않은 애플리케이션의 경우 일반적으로 고려할 필요가 없습니다! 일반적인 파일 조작 방법에는 전혀 문제가 없습니다. 하지만 동시성이 높으면 파일을 읽고 쓸 때 다음 파일에 대해 여러 프로세스가 작동할 가능성이 높습니다. 이때 파일에 대한 액세스가 배타적이지 않으면 쉽게 데이터 손실이 발생할 수 있습니다

최적화 플랜 4 : 논블로킹 파일 전용 잠금 사용

<?php
//优化方案4:使用非阻塞的文件排他锁
include (&#39;./mysql.php&#39;);
//生成唯一订单号
function build_order_no(){
  return date(&#39;ymd&#39;).substr(implode(NULL, array_map(&#39;ord&#39;, str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
}
//记录日志
function insertLog($event,$type=0){
    global $conn;
    $sql="insert into ih_log(event,type)
    values(&#39;$event&#39;,&#39;$type&#39;)";
    mysqli_query($conn,$sql);
}
$fp = fopen("lock.txt", "w+");
if(!flock($fp,LOCK_EX | LOCK_NB)){
    echo "系统繁忙,请稍后再试";
    return;
}
//下单
$sql="select number from ih_store where goods_id=&#39;$goods_id&#39; and sku_id=&#39;$sku_id&#39;";
$rs =  mysqli_query($conn,$sql);
$row = $rs->fetch_assoc();
if($row[&#39;number&#39;]>0){//库存是否大于0
    //模拟下单操作
    $order_sn=build_order_no();
    $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price)
    values(&#39;$order_sn&#39;,&#39;$user_id&#39;,&#39;$goods_id&#39;,&#39;$sku_id&#39;,&#39;$price&#39;)";
    $order_rs =  mysqli_query($conn,$sql);
    //库存减少
    $sql="update ih_store set number=number-{$number} where sku_id=&#39;$sku_id&#39;";
    $store_rs =  mysqli_query($conn,$sql);
    if($store_rs){
      echo &#39;库存减少成功&#39;;
        insertLog(&#39;库存减少成功&#39;);
        flock($fp,LOCK_UN);//释放锁
    }else{
      echo &#39;库存减少失败&#39;;
        insertLog(&#39;库存减少失败&#39;);
    }
}else{
  echo &#39;库存不够&#39;;
    insertLog(&#39;库存不够&#39;);
}
fclose($fp);
 ?>

위 내용은 참고용입니다!

추천 튜토리얼: PHP 비디오 튜토리얼

위 내용은 PHP는 높은 동시성을 어떻게 해결합니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.