關於PHP高併發搶購系統設計

weixin_34162629發表於2017-06-08
內容
併發搶購系統注意事項
高併發架構設計描述
程式端核心程式碼實現
訂單流程mysql 端併發解決方案


注意事項
(1)高併發環境下,對於伺服器cup、記憶體、網路寬頻使用率會瞬間暴漲,需要注意對同伺服器上其他應用的影響。(專案解耦,高併發應用獨立部署)
(2)伺服器高負載執行,容易出現當機,重啟伺服器場景,要提前考慮記憶體(redis)資料備份與恢復,防止使用者搶購資料丟失.
(3)高併發應用首先要注重穩定性,其次是效能上優化.


(4) 一臺伺服器能夠支援多少併發量
nginx服務為例:
worker_processes 8;
worker_rlimit_nofile 102400;
use epoll;
worker_connections 102400;
ulimit -n
cat /proc/sys/fs/file-max 


架構設計

(1)LVS服務, 做負載均衡排程, 採用RD模式, 通過股修改資料包的目的MAC地址實現轉發,該方式效能好, 對併發高應用,適合大規模部署負載均衡機器;抗負載能力強、是工作在網路4層僅作分發之用,沒有流量的產生;工作穩定,自身有完整的雙機熱備方案
(2)keepalive(vrrp協議方式) 做心跳檢測,支援應用具有高可用性。
(3)nginx工作在網路的7層,所以它可以針對http應用本身來做分流策略, 可用說對LVS負載的補充。nginx高效處理高併發請求在於採用非同步非阻塞工作方式和epoll IO 模型。  
(4)頁面動態資料,使用者資料,搶購商品資料採用Redis儲存。
(5)使用者搶購記錄標識儲存在Redis伺服器端。在nginx負載均衡端,應用lua指令碼做使用者搶購記錄過濾。
(6)real server端部署 nginx與php, 同時 real server 可以參與負載端排程。
(7)mysql server cluster 端採用一主多從部署,master負載資料寫及同步到slave, slave負責資料讀取。推薦應用mysql代理元件atlas, 實現對php端對mysql讀寫透明操作。
核心程式碼實現
背景
假設每個使用者只允許搶購一件商品。
預備資料
搶購商品總數存入redis中, 比如十萬個資料
$redisObj = new redis();
$redisObj->set('goods_amount', 1000000);
$redis->watch('goods_amount'); //應用redis watch 樂觀鎖
$amount = $redis->get('goods_amount');
if($amount > 0)
{
 $userInfo = $reids->get('user_info_crc32(url_token)', array('userId'=>120, '....')); 
 
  if(empty($userInfo)){
      
        $ret = $redis->multi() ->decr('goods_amount') ->exec();
      if($ret){
$reids->set('user_info_crc32(url_token)', array('userId'=>120, '....')); 
       根據crc32(url_token)唯一索引建立改使用者已搶過商品的標識。(同時標識可以設定一段時間有效期,例如10分鐘);
    
write("user_id", {user_id}_success.log);
      }else {
                //提示搶購失敗 
    }
 } else {
    $redis->unwatch(‘goods_amount');
    //提示搶購失敗 
} else {
    //搶購結束, 封閉入口
}
}


(1)下一個搶購請求到來時,在nginx伺服器lua端,檢查googs_amount搶購商品數量,判斷搶購有沒有結束,在判斷user_info_crc32(url_token)有沒有搶過成功,如果成功跳轉到下單頁面,否則執行搶過流程。
(2)搶購首頁直接高併發靜態資源儲存在cdn 服務端, 來減輕服務端訪問請求的壓力
mysql端併發解決
(1)搶購商品資料預熱,提前儲存在redis中,比如商品名稱,屬性等等。
(2)採用innodb 資料庫引擎,在高併發場景讀操作有優勢,合理建立表結構,儘可能的減少連結串列查,可以適當設計表中冗餘欄位,sql查詢能夠必須走索引。
(3)使用者瀏覽商品詳情頁(需要在redis端做動態資料快取) 
(4)使用者點選購買跳轉到訂單詳情頁(包括使用者基本資訊,商品資訊,支付方式,積分消費等資料考慮對資料庫併發查詢壓力,要採用redis快取策略)
(5)訂單資料提前生成,user_id留空,同時通過redis lpush,把連續訂單id,提前同步到redis分散式叢集,redis叢集支援心調檢測,能夠自動做服務奔潰切換。
(6)使用者提交訂單後,  在redis服務lpop拿到一個訂單id, 根據訂單id條件更新使用者user_id等資訊。
   begin;
   update mt_account set user_id=100 where order_id=$orderId and user_id=0 li mit 1;
   commit;
 

相關文章