本文轉自:http://www.javashuo.com/article/p-ooiioipc-my.htmlphp
秒殺系統難作的緣由:庫存只有一份,全部人會在集中的時間讀和寫這些數據。例如小米手機每週二的秒殺,可能手機只有1萬部,但瞬時進入的流量多是幾百幾千萬。又例如12306搶票,亦與秒殺相似,瞬時流量更甚。html
主要須要解決的問題有兩個:mysql
超賣
問題) 對於第一個問題,已經很容易想到用緩存來處理搶購,避免直接操做數據庫,例如使用Redis。重點在於第二個問題,常規寫法:
查詢出對應商品的庫存,看是否大於0,而後執行生成訂單等操做,可是在判斷庫存是否大於0處,若是在高併發下就會有問題,致使庫存量出現負數程序員
流量到了億級別,常見站點架構如上:redis
1)將請求儘可能攔截在系統上游:傳統秒殺系統之因此掛,請求都壓倒了後端數據層,數據讀寫鎖衝突嚴重,併發高響應慢,幾乎全部請求都超時,流量雖大,下單成功的有效流量甚小【一趟火車其實只有2000張票,200w我的來買,基本沒有人能買成功,請求有效率爲0】
2)充分利用緩存:這是一個典型的讀多寫少
的應用場景【一趟火車其實只有2000張票,200w我的來買,最多2000我的下單成功,其餘人都是查詢庫存,寫比例只有0.1%,讀比例佔99.9%】,很是適合使用緩存。sql
4.1)瀏覽器層請求攔截數據庫
點擊了「查詢」按鈕以後,系統那個卡呀,進度條漲的慢呀,做爲用戶,我會不自覺的再去點擊「查詢」,繼續點,繼續點,點點點。。。有用麼?無緣無故的增長了系統負載(一個用戶點5次,80%的請求是這麼多出來的),怎麼整?segmentfault
a 產品層面,用戶點擊「查詢」或者「購票」後,按鈕置灰,禁止用戶重複提交請求
b JS層面,限制用戶在x秒以內只能提交一次請求後端
如此限流,80%流量已攔。
瀏覽器
4.2)站點層請求攔截與頁面緩存
瀏覽器層的請求攔截,只能攔住小白用戶(不過這是99%的用戶喲),高端的程序員根本不吃這一套,寫個for循環,直接調用你後端的http請求,怎麼整?
a 同一個uid,限制訪問頻度,作頁面緩存,x秒內到達站點層的請求,均返回同一頁面
b 同一個item的查詢,例如手機車次,作頁面緩存,x秒內到達站點層的請求,均返回同一頁面
如此限流,又有99%的流量會被攔截在站點層
4.3)服務層請求攔截與數據緩存
站點層的請求攔截,只能攔住普通程序員,高級黑客,假設他控制了10w臺肉雞(而且假設買票不須要實名認證),這下uid的限制不行了吧?怎麼整?
a 大哥,我是服務層,我清楚的知道小米只有1萬部手機,我清楚的知道一列火車只有2000張車票,我透10w個請求去數據庫有什麼意義呢?對於寫請求,作請求隊列
,每次只透有限的寫請求去數據層,若是均成功再放下一批,若是庫存不夠則隊列裏的寫請求所有返回「已售完」
b 對於讀請求
,還要我說麼?cache抗,不論是memcached仍是redis,單機抗個每秒10w應該都是沒什麼問題的
如此限流,只有很是少的寫請求,和很是少的讀緩存mis的請求會透到數據層去,又有99.9%的請求被攔住了
4.4)數據層閒庭信步
到了數據這一層,幾乎就沒有什麼請求了,單機也能扛得住,仍是那句話,庫存是有限的,小米的產能有限,透這麼多請求來數據庫沒有意義。
4.5)mysql批量入庫提升INSERT效率
使用redis
隊列(list
),push
和pop
操做保證了原子性
的實現。即便有不少用戶同時到達,也是依次執行。(mysql事務在高併發下性能降低很厲害)
先將商品庫存存入隊列:
<?php $store=1000; //商品庫存 $redis=new Redis(); $result=$redis->connect('127.0.0.1',6379); $res=$redis->llen('goods_store'); for($i=0; $i<$store; $i++){ $redis->lpush('goods_store',1); } echo $redis->llen('goods_store'); ?>
客戶執行下單操做:
$redis=new Redis(); $result=$redis->connect('127.0.0.1',6379); $count = $redis->lpop('goods_store'); if(!$count){ echo '搶購失敗!'; return; }
緩存也是能夠應對寫請求的,好比咱們就能夠把數據庫中的庫存數據轉移到Redis緩存中,全部減庫存操做都在Redis中進行,而後再經過後臺進程把Redis中的用戶秒殺請求同步到數據庫中
沒什麼總結了,上文應該描述的很是清楚了,對於秒殺系統,再次重複下兩個架構優化思路:1)儘可能將請求攔截在系統上游
2)讀多寫少經量多使用緩存
3) redis隊列緩存 + mysql 批量入庫
參考文檔1:秒殺系統架構優化思路
參考文檔2:【高併發簡單解決方案】redis隊列緩存 + mysql 批量入庫 + php離線整合
參考文檔3:PHP+Mysql高併發解決
參考文檔4:php結合redis實現高併發下的搶購、秒殺功能