높은 동시성에서의 데이터 보안
우리는 여러 스레드가 동일한 파일에 쓸 때 "스레드 안전" 문제가 발생한다는 것을 알고 있습니다. run 결과는 단일 스레드 작업의 결과와 동일합니다. 즉, 스레드로부터 안전하다는 의미입니다. MySQL 데이터베이스인 경우 자체 잠금 메커니즘을 사용하여 문제를 해결할 수 있습니다. 그러나 대규모 동시성 시나리오에서는 MySQL이 권장되지 않습니다.
플래시세일 및 러시세일 시나리오에는 또 다른 문제가 있는데, 바로 '과잉 전송'입니다. 이 부분을 주의 깊게 관리하지 않으면 과도한 전송이 발생하게 됩니다. 또한 일부 전자상거래 회사에서는 구매자가 상품을 성공적으로 구매한 후에도 판매자가 주문이 유효한 것으로 인식하지 못하고 상품 배송을 거부하는 경우가 있다고 들었습니다. 여기서 문제는 반드시 가맹점이 배신적이라는 것이 아니라, 시스템의 기술적 수준에서 과잉 발행의 위험이 발생한다는 점일 수 있습니다.
1. 초과배송 이유
급하게 구매하는 상황에서 총 100개의 제품이 있다고 가정해 보겠습니다. 마지막 순간에 99개의 제품을 소비하고 마지막 제품만 남았습니다. 이때 시스템은 여러 개의 동시 요청을 보냈고, 이러한 요청으로 읽은 상품 잔액은 모두 99개였으며 모두 이 잔액 판단을 통과하여 결국 초과 발행으로 이어졌습니다. (앞서 기사에서 언급한 장면과 동일)
위 그림에서는 동시유저 B도 '구매성공'을 하게 되어, 한명이 더 상품을 획득하게 되었습니다. 이 시나리오는 동시성이 높은 상황에서 발생하기가 매우 쉽습니다.
최적화 계획 1: 인벤토리 필드 번호 필드를 unsigned로 설정합니다. 인벤토리가 0이면 필드가 음수가 될 수 없으므로 false가 반환됩니다.
<?php //优化方案1:将库存字段number字段设为unsigned,当库存为0时,因为字段不能为负数,将会返回false include('./mysql.php'); $username = 'wang'.rand(0,1000); //生成唯一订单 function build_order_no(){ return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8); } //记录日志 function insertLog($event,$type=0,$username){ global $conn; $sql="insert into ih_log(event,type,usernma) values('$event','$type','$username')"; return mysqli_query($conn,$sql); } function insertOrder($order_sn,$user_id,$goods_id,$sku_id,$price,$username,$number) { global $conn; $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price,username,number) values('$order_sn','$user_id','$goods_id','$sku_id','$price','$username','$number')"; return mysqli_query($conn,$sql); } //模拟下单操作 //库存是否大于0 $sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id' "; $rs=mysqli_query($conn,$sql); $row = $rs->fetch_assoc(); if($row['number']>0){//高并发下会导致超卖 if($row['number']<$number){ return insertLog('库存不够',3,$username); } $order_sn=build_order_no(); //库存减少 $sql="update ih_store set number=number-{$number} where sku_id='$sku_id' and number>0"; $store_rs=mysqli_query($conn,$sql); if($store_rs){ //生成订单 insertOrder($order_sn,$user_id,$goods_id,$sku_id,$price,$username,$number); insertLog('库存减少成功',1,$username); }else{ insertLog('库存减少失败',2,$username); } }else{ insertLog('库存不够',3,$username); } ?>
2.
동시성이 높거나 낮은 일본 IP 애플리케이션에는 일반적으로 이러한 사항을 고려할 필요가 없습니다! 일반적인 파일 조작 방법에는 전혀 문제가 없습니다. 그러나 동시성이 높으면 파일을 읽고 쓸 때 다음 파일에 대해 여러 프로세스가 작동할 가능성이 매우 높습니다. 이때 파일에 대한 액세스가 배타적이지 않으면 쉽게 데이터 손실이 발생할 수 있습니다.최적화 솔루션 4: 비차단 파일 독점 잠금 사용
<?php //优化方案4:使用非阻塞的文件排他锁 include ('./mysql.php'); //生成唯一订单号 function build_order_no(){ return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8); } //记录日志 function insertLog($event,$type=0){ global $conn; $sql="insert into ih_log(event,type) values('$event','$type')"; 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='$goods_id' and sku_id='$sku_id'"; $rs = mysqli_query($conn,$sql); $row = $rs->fetch_assoc(); if($row['number']>0){//库存是否大于0 //模拟下单操作 $order_sn=build_order_no(); $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price) values('$order_sn','$user_id','$goods_id','$sku_id','$price')"; $order_rs = mysqli_query($conn,$sql); //库存减少 $sql="update ih_store set number=number-{$number} where sku_id='$sku_id'"; $store_rs = mysqli_query($conn,$sql); if($store_rs){ echo '库存减少成功'; insertLog('库存减少成功'); flock($fp,LOCK_UN);//释放锁 }else{ echo '库存减少失败'; insertLog('库存减少失败'); } }else{ echo '库存不够'; insertLog('库存不够'); } fclose($fp); ?>추천 튜토리얼:
위 내용은 PHP는 높은 동시성을 어떻게 처리합니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!