php結合redis實現高並發下的搶購、秒殺功能

來源:互聯網
上載者:User

標籤:非阻塞   產生   date   null   connect   不能   檔案鎖   記錄   思路   

搶購、秒殺是如今很常見的一個應用情境,主要需要解決的問題有兩個: 1 高並發對資料庫產生的壓力 2 競爭狀態下如何解決庫存的正確減少("超賣"問題) 對於第一個問題,已經很容易想到用緩衝來處理搶購,避免直接操作資料庫,例如使用Redis。 重點在於第二個問題 最佳化方案1:將庫存欄位number欄位設為unsigned,當庫存為0時,因為欄位不能為負數,將會返回false(略) 最佳化方案2:使用的事務,鎖住操作的行

<?php$conn=mysql_connect("localhost","big","123456");if(!$conn){    echo "connect failed";    exit;}mysql_select_db("big",$conn);mysql_query("set names utf8");$price=10;$user_id=1;$goods_id=1;$sku_id=11;$number=1;//產生唯一訂單號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‘)";    mysql_query($sql,$conn);}//類比下單操作//庫存是否大於0mysql_query("BEGIN");   //開始事務$sql="select number from ih_store where goods_id=‘$goods_id‘ and sku_id=‘$sku_id‘ FOR UPDATE";//此時這條記錄被鎖住,其它事務必須等待此次事務提交後才能執行$rs=mysql_query($sql,$conn);$row=mysql_fetch_assoc($rs);if($row[‘number‘]>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=mysql_query($sql,$conn);    //庫存減少    $sql="update ih_store set number=number-{$number} where sku_id=‘$sku_id‘";    $store_rs=mysql_query($sql,$conn);    if(mysql_affected_rows()){        insertLog(‘庫存減少成功‘);        mysql_query("COMMIT");//事務提交即解鎖    }else{        insertLog(‘庫存減少失敗‘);    }}else{    insertLog(‘庫存不夠‘);    mysql_query("ROLLBACK");}?>

 最佳化方案3:使用非阻塞的檔案獨佔鎖定

<?php$conn=mysql_connect("localhost","root","123456");if(!$conn){    echo "connect failed";    exit;}mysql_select_db("big-bak",$conn);mysql_query("set names utf8");$price=10;$user_id=1;$goods_id=1;$sku_id=11;$number=1;//產生唯一訂單號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‘)";    mysql_query($sql,$conn);}$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=mysql_query($sql,$conn);$row=mysql_fetch_assoc($rs);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=mysql_query($sql,$conn);    //庫存減少    $sql="update ih_store set number=number-{$number} where sku_id=‘$sku_id‘";    $store_rs=mysql_query($sql,$conn);    if(mysql_affected_rows()){        insertLog(‘庫存減少成功‘);        flock($fp,LOCK_UN);//釋放鎖    }else{        insertLog(‘庫存減少失敗‘);    }}else{    insertLog(‘庫存不夠‘);}fclose($fp);

 最佳化方案4:使用redis隊列,因為pop操作是原子的,即使有很多使用者同時到達,也是依次執行,推薦使用(mysql事務在高並發下效能下降很厲害,檔案鎖的方式也是)
先將商品庫存如隊列

<?php$store=1000;$redis=new Redis();$result=$redis->connect(‘127.0.0.1‘,6379);$res=$redis->llen(‘goods_store‘);echo $res;$count=$store-$res;for($i=0;$i<$count;$i++){    $redis->lpush(‘goods_store‘,1);}echo $redis->llen(‘goods_store‘);?>搶購、描述邏輯 <?php$conn=mysql_connect("localhost","big","123456");if(!$conn){    echo "connect failed";    exit;}mysql_select_db("big",$conn);mysql_query("set names utf8");$price=10;$user_id=1;$goods_id=1;$sku_id=11;$number=1;//產生唯一訂單號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‘)";    mysql_query($sql,$conn);}//類比下單操作//下單前判斷redis隊列庫存量$redis=new Redis();$result=$redis->connect(‘127.0.0.1‘,6379);$count=$redis->lpop(‘goods_store‘);if(!$count){    insertLog(‘error:no store redis‘);    return;}//產生訂單$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=mysql_query($sql,$conn);//庫存減少$sql="update ih_store set number=number-{$number} where sku_id=‘$sku_id‘";$store_rs=mysql_query($sql,$conn);if(mysql_affected_rows()){    insertLog(‘庫存減少成功‘);}else{    insertLog(‘庫存減少失敗‘);}上述只是簡單類比高並發下的搶購,真實情境要比這複雜很多,很多注意的地方如搶購頁面做成靜態,通過ajax調用介面再如上面的會導致一個使用者搶多個,思路:需要一個排隊隊列和搶購結果隊列及庫存隊列。高並發情況,先將使用者進入排隊隊列,用一個線程迴圈處理從排隊隊列取出一個使用者,判斷使用者是否已在搶購結果隊列,如果在,則已搶購,否則未搶購,庫存減1,寫資料庫,將使用者入結果隊列。

 

php結合redis實現高並發下的搶購、秒殺功能

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.