高同時実行時のデータ セキュリティ
複数のスレッドが同じファイルに書き込む場合、「スレッド セーフ」が存在することがわかっています (複数のスレッド同じコードを同時に実行します。各実行の結果が単一スレッドの実行の結果と同じであり、結果が期待どおりであれば、スレッドセーフです)。 MySQL データベースの場合は、独自のロック メカニズムを使用して問題を解決できますが、大規模な同時実行シナリオでは MySQL はお勧めできません。
フラッシュ セールスや飛び込み販売のシナリオでは、「過剰送信」という問題がもう 1 つあります。この点を慎重に制御しないと、過剰な送信が発生します。また、一部の電子商取引会社においては、購入者が商品を購入したにもかかわらず、販売者が注文を有効なものとして認識せず、商品の配送を拒否するという駆け込み購入行為を行っているという話も聞いております。ここでの問題は、必ずしも販売業者が不正であるということではなく、システムの技術レベルでの過剰発行のリスクによって引き起こされる可能性があります。
1. 過剰納品の理由
ある急ぎ購入シナリオでは、製品が合計で 100 個しかなく、最後の瞬間に、 99 個の商品を消費しました。最後の 1 個だけが残っています。このとき、システムは複数のリクエストを同時に送信し、そのリクエストで読み取った商品残高が99個すべてで残高判定を通過し、最終的に発行過剰に陥ってしまいました。 (記事の前半で説明したシーンと同じです)
上の図では、同時ユーザー B も「購入に成功」し、さらに 1 人が購入できるようになりました。製品。このシナリオは、同時実行性が高い状況で非常に発生しやすくなります。
最適化計画 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 は高い同時実行性をどのように処理しますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。