內(nèi)容
并發(fā)搶購系統(tǒng)注意事項(xiàng)
高并發(fā)架構(gòu)設(shè)計(jì)描述
程序端核心代碼實(shí)現(xiàn)
訂單流程mysql 端并發(fā)解決方案
注意事項(xiàng)
(1)高并發(fā)環(huán)境下,對(duì)于服務(wù)器cup、內(nèi)存、網(wǎng)絡(luò)寬帶使用率會(huì)瞬間暴漲,需要注意對(duì)同服務(wù)器上其他應(yīng)用的影響。(項(xiàng)目解耦,高并發(fā)應(yīng)用獨(dú)立部署)
(2)服務(wù)器高負(fù)載運(yùn)行,容易出現(xiàn)死機(jī),重啟服務(wù)器場景,要提前考慮內(nèi)存(redis)數(shù)據(jù)備份與恢復(fù),防止用戶搶購數(shù)據(jù)丟失.
(3)高并發(fā)應(yīng)用首先要注重穩(wěn)定性,其次是性能上優(yōu)化.
(4) 一臺(tái)服務(wù)器能夠支持多少并發(fā)量
nginx服務(wù)為例:
worker_processes 8;
worker_rlimit_nofile 102400;
use epoll;
worker_connections 102400;
ulimit -n
cat /proc/sys/fs/file-max
架構(gòu)設(shè)計(jì)
(1)LVS服務(wù), 做負(fù)載均衡調(diào)度, 采用RD模式, 通過股修改數(shù)據(jù)包的目的MAC地址實(shí)現(xiàn)轉(zhuǎn)發(fā),該方式性能好, 對(duì)并發(fā)高應(yīng)用,適合大規(guī)模部署負(fù)載均衡機(jī)器;抗負(fù)載能力強(qiáng)、是工作在網(wǎng)絡(luò)4層僅作分發(fā)之用,沒有流量的產(chǎn)生;工作穩(wěn)定,自身有完整的雙機(jī)熱備方案
(2)keepalive(vrrp協(xié)議方式) 做心跳檢測,支持應(yīng)用具有高可用性。
(3)nginx工作在網(wǎng)絡(luò)的7層,所以它可以針對(duì)http應(yīng)用本身來做分流策略, 可用說對(duì)LVS負(fù)載的補(bǔ)充。nginx高效處理高并發(fā)請(qǐng)求在于采用異步非阻塞工作方式和epoll IO 模型。
(4)頁面動(dòng)態(tài)數(shù)據(jù),用戶數(shù)據(jù),搶購商品數(shù)據(jù)采用Redis存儲(chǔ)。
(5)用戶搶購記錄標(biāo)識(shí)存儲(chǔ)在Redis服務(wù)器端。在nginx負(fù)載均衡端,應(yīng)用lua腳本做用戶搶購記錄過濾。
(6)real server端部署 nginx與php, 同時(shí) real server 可以參與負(fù)載端調(diào)度。
(7)mysql server cluster 端采用一主多從部署,master負(fù)載數(shù)據(jù)寫及同步到slave, slave負(fù)責(zé)數(shù)據(jù)讀取。推薦應(yīng)用mysql代理組件atlas, 實(shí)現(xiàn)對(duì)php端對(duì)mysql讀寫透明操作。
核心代碼實(shí)現(xiàn)
背景
假設(shè)每個(gè)用戶只允許搶購一件商品。
預(yù)備數(shù)據(jù)
搶購商品總數(shù)存入redis中, 比如十萬個(gè)數(shù)據(jù)
$redisObj = new redis();
$redisObj->set('goods_amount', 1000000);
$redis->watch('goods_amount'); //應(yīng)用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, '....'));
根據(jù)crc32(url_token)唯一索引創(chuàng)建改用戶已搶過商品的標(biāo)識(shí)。(同時(shí)標(biāo)識(shí)可以設(shè)置一段時(shí)間有效期,例如10分鐘);
write("user_id", {user_id}_success.log);
}else {
//提示搶購失敗
}
} else {
$redis->unwatch(‘goods_amount');
//提示搶購失敗
} else {
//搶購結(jié)束, 封閉入口
}
}
(1)下一個(gè)搶購請(qǐng)求到來時(shí),在nginx服務(wù)器lua端,檢查googs_amount搶購商品數(shù)量,判斷搶購有沒有結(jié)束,在判斷user_info_crc32(url_token)有沒有搶過成功,如果成功跳轉(zhuǎn)到下單頁面,否則執(zhí)行搶過流程。
(2)搶購首頁直接高并發(fā)靜態(tài)資源存儲(chǔ)在cdn 服務(wù)端, 來減輕服務(wù)端訪問請(qǐng)求的壓力
mysql端并發(fā)解決
(1)搶購商品數(shù)據(jù)預(yù)熱,提前存儲(chǔ)在redis中,比如商品名稱,屬性等等。
(2)采用innodb 數(shù)據(jù)庫引擎,在高并發(fā)場景讀操作有優(yōu)勢,合理創(chuàng)建表結(jié)構(gòu),盡可能的減少鏈表查,可以適當(dāng)設(shè)計(jì)表中冗余字段,sql查詢能夠必須走索引。
(3)用戶瀏覽商品詳情頁(需要在redis端做動(dòng)態(tài)數(shù)據(jù)緩存)
(4)用戶點(diǎn)擊購買跳轉(zhuǎn)到訂單詳情頁(包括用戶基本信息,商品信息,支付方式,積分消費(fèi)等數(shù)據(jù)考慮對(duì)數(shù)據(jù)庫并發(fā)查詢壓力,要采用redis緩存策略)
(5)訂單數(shù)據(jù)提前生成,user_id留空,同時(shí)通過redis lpush,把連續(xù)訂單id,提前同步到redis分布式集群,redis集群支持心調(diào)檢測,能夠自動(dòng)做服務(wù)奔潰切換。
(6)用戶提交訂單后, 在redis服務(wù)lpop拿到一個(gè)訂單id, 根據(jù)訂單id條件更新用戶user_id等信息。
begin;
update mt_account set user_id=100 where order_id=$orderId and user_id=0 li mit 1;
commit;