搜尋
首頁後端開發php教程分享php秒殺功能實現的思路

分享php秒殺功能實現的思路

推薦:《PHP影片教學》 

一、秒殺業務為什麼難做

1)im系統,例如qq或微博,每個人都讀自己的資料(好友列表、群列表、個人資訊);

2)微博系統,每個人讀你關注的人的數據,一個人讀多個人的數據

#3)秒殺系統,庫存只有一份,所有人會在集中的時間讀取和寫這些數據,多個人讀一個數據

例如:小米手機每週二的秒殺,可能手機只有1萬部,但瞬時進入的流量可能是幾百幾千萬。

又例如:12306搶票,票是有限的,庫存一份,瞬時流量非常多,都讀相同的庫存。 讀寫衝突,鎖非常嚴重,這是秒殺業務困難的地方。那我們要怎麼優化秒殺業務的架構呢?

二、最佳化方向

最佳化方向有兩個(今天就講這兩個點):

(1)將請求盡量攔截在系統上游(不要讓鎖定衝突落到資料庫上去)。傳統秒殺系統之所以掛,請求都壓倒了後端資料層,資料讀寫鎖定衝突嚴重,並發高回應慢,幾乎所有請求都超時,流量雖大,下單成功的有效流量甚小。以12306為例,一班火車其實只有2000張票,200w個人來買,基本上沒有人能買成功,請求有效率為0。

(2)充分利用快取,秒殺買票,這是一個典型的讀多些少的應用場景,大部分請求是車次查詢,票查詢,下單和支付才是寫請求。一班火車其實只有2000張票,200w個人來買,最多2000個人下單成功,其他人都是查詢庫存,寫比例只有0.1%,讀比例佔99.9%,非常適合使用緩存來優化。好,後續講講怎麼個「將請求盡量攔截在系統上游」法,以及怎麼個「快取」法,講講細節。

三、常見秒殺架構

常見的網站架構基本上是這樣的(絕對不畫忽悠類別的架構圖)


(1)瀏覽器端,最上層,會執行到一些JS程式碼

(2)服務端,這一層會存取後端數據,拼html頁面回傳給瀏覽器

(3)服務層(web伺服器),向上游屏蔽底層資料細節,提供資料存取

(4)資料層,最終的庫存是存在這裡的,mysql是一個典型(當然還有會緩存)

這個圖雖然簡單,但能形象的說明大流量高並發的秒殺業務架構,大家要記得這張圖。

後面要細解析各層級怎麼優化。

四、各層次優化細節

第一層,客戶端怎麼優化(瀏覽器層,APP層)

問大家一個問題,大家都玩過微信的搖一搖搶紅包對吧,每次搖一搖,就會往後端發送請求麼?回顧我們下單搶票的場景,點擊了“查詢”按鈕之後,系統那個卡呀,進度條漲的慢呀,作為用戶,我會不自覺的再去點擊“查詢”,對麼?繼續點,繼續點,點點點。 。 。有用麼?平白無故的增加了系統負載,一個用戶點5次,80%的請求是這麼多出來的,怎麼整?

(a)產品層面,使用者點選「查詢」或「購票」後,按鈕置灰,禁止使用者重複提交請求;

(b)JS層面,限制使用者在x秒之內只能提交一次請求

APP層面,可以做類似的事情,雖然你瘋狂的在搖微信,其實x秒才向後端發起一次請求。這就是所謂的“將請求盡量攔截在系統上游”,越上游越好,瀏覽器層,APP層就給攔住,這樣就能擋住80% 的請求,這種辦法只能攔住普通用戶(但99%的用戶是普通用戶)對於群組內的高階程式設計師是攔不住的。 firebug一抓包,http長啥樣都知道,js是萬萬攔不住程式設計師寫for循環,呼叫http介面的,這部分請求怎麼處理?

第二層,服務端層面的請求攔截

怎麼攔截? 怎麼防止程式設計師寫for循環呼叫,有去重依據麼? ip? cookie-id? …想複雜了,這類業務都需要登錄,用uid即可。在服務端層面,對uid進行請求計數和去重,甚至不需要統一儲存計數,直接服務端記憶體儲存(這樣計數會不准,但最簡單)。一個uid,5秒只準透過1個請求,這樣又能攔住99%的for循環請求。

5s只透過一個請求,其餘的請求怎麼辦?緩存,頁面緩存,同一個uid,限制訪問頻度,做頁面緩存,x秒內到達服務端的請求,均返回同一頁。同一個item的查詢,例如車次,做頁面緩存,x秒內到達服務端的請求,均返回同一頁。如此限流,既能保證用戶有良好的用戶體驗(沒有返回404)又能保證系統的健壯性(利用頁面緩存,把請求攔截在服務端了) 。

頁面快取不一定要保證所有服務端傳回一致的頁面,直接放在每個網站的記憶體也是可以的。優點是簡單,壞處是http請求落到不同的服務端,回傳的車票資料可能不一樣,這是服務端的請求攔截與快取優化。

好,這個方式攔住了寫for循環發http請求的程式設計師,有些高階程式設計師(駭客)控制了10w個肉雞,手裡有10w個uid,同時發請求(先不考慮實名制的問題,小米搶手機不需要實名制),這下怎麼辦,服務端按照uid限流攔不住了。

第三層 服務層來攔截(反正就是不要讓請求落到資料庫上去)

服務層怎麼攔截?大哥,我是服務層,我清楚的知道小米只有1萬支手機,我清楚的知道一列火車只有2000張車票,我透10w個請求去數據庫有什麼意義呢?沒錯,請求隊列!

對於寫入請求,做請求隊列,每次只透有限的寫入請求去資料層(下訂單,支付這樣的寫業務)

1w部手機,只透1w個下單請求去db

3k張火車票,只透3k個下單請求去db

如果均成功再放下一批,如果庫存不夠則隊列裡的寫請求全部返回「已售完」。

對於讀取請求,怎麼優化? cache抗,不管是memcached還是redis,單機抗個每秒10w應該都是沒什麼問題的。如此限流,只有非常少的寫入請求,和非常少的讀取快取mis的請求會透到資料層去,又有99.9%的請求被攔住了。

當然,還有業務規則上的一些最佳化。回想12306所做的,分時分段售票,原來統一10點賣票,現在8點,8點半,9點,...每隔半小時放出一批:將流量攤勻。

其次,資料粒度的最佳化:你去購票,對於餘票查詢這個業務,票剩了58張,還是26張,你真的關注麼,其實我們只關心有票和無票?流量大的時候,做一個粗粒度的「有票」「無票」快取即可。

第三,一些業務邏輯的非同步:例如下單業務與 支付業務的分離。這些最佳化都是結合 業務 來的,我之前分享過一個觀點「一切脫離業務的架構設計都是耍流氓」架構的最佳化也要針對業務。

好了,最後是資料庫層

瀏覽器攔截了80%,服務端攔截了99.9%並做了頁面緩存,服務層又做了寫請求佇列與資料緩存,每次透到資料庫層的請求都是可控的。 db基本上就沒什麼壓力了,閒庭信步,單機也能扛得住,還是那句話,庫存是有限的,小米的產能有限,透這麼多請求來數據庫沒有意義。

全部透到資料庫,100w個下單,0個成功,請求有效率0%。透3k個到數據,全部成功,請求有效率100%。

五、總結

上文應該描述的非常清楚了,沒什麼總結了,對於秒殺系統,再次重複下我個人經驗的兩個架構優化思路:

(1)盡量將請求攔截在系統上游(越上游越好);

(2)讀多寫少的常用多使用快取(快取抗讀壓力);

瀏覽器與APP:做限速

服務端:依照uid做限速,做頁面快取

服務層(網頁伺服器):依照業務做寫入要求佇列控制流量,做資料快取

資料層:閒庭訊號步驟

並且:結合業務做最佳化

六、Q&A

問題1、按你的架構,其實壓力最大的反而是服務端,假設真實有效的請求數有1000萬,不太可能限制請求連接數吧,那麼這部分的壓力怎麼處理?

答:每秒鐘的並發可能沒有1kw,假設有1kw,解決方案2個:

(1)服務層(web伺服器)是可以透過加機器擴容的,最不濟1k台機器來嗆。

(2)如果機器不夠,拋棄請求,拋棄50%(50%直接回傳稍後再試),原則是要保護系統,不能讓所有使用者都失敗。

問題2、「控制了10w個肉雞,手上有10w個uid,同時發請求」 這個問題怎麼解決哈?

答:上面說了,服務層(web伺服器)寫入請求佇列控制

問題3:限制存取頻次的緩存,是否也可以用來搜尋?例如A用戶搜尋了“手機”,B用戶搜尋“手機”,優先使用A搜尋後產生的快取頁面?

答:這個是可以的,這個方法也常用在「動態」營運活動頁,例如短時間推送4kw用戶app-push營運活動,做頁面快取。

問題4:如果佇列處理失敗,該如何處理?肉雞把隊列被撐爆了怎麼辦?

答案:處理失敗回傳下單失敗,讓使用者再試一次。隊列成本很低,爆了很難吧。在最壞的情況下,快取了若干請求之後,後續請求都直接回傳「無票」(佇列裡已經有100w請求了,都等著,再接受請求也沒有意義了)

問題5 :服務端過濾的話,是把uid請求數單獨儲存到各個網站的記憶體中麼?如果是這樣的話,怎麼處理多台伺服器叢集經過負載平衡器將相同使用者的回應分散到不同伺服器的情況呢?還是說將服務端的過濾放到負載平衡前?

答:可以放在內存,這樣的話看似一台伺服器限制了5s一個請求,全局來說(假設有10台機器),其實是限制了5s 10個請求,解決辦法:

1)加大限制(這是建議的方案,最簡單)

2)在nginx層做7層均衡,讓一個uid的請求盡量落到同一個機器上

問題6:服務層(網頁伺服器)過濾的話,佇列是服務層(網頁伺服器)統一的一個佇列?還是每個提供服務的伺服器各一個佇列?如果是統一的一個佇列的話,需不需要在各伺服器提交的請求入佇列前進行鎖定控制?

答:可以不用統一一個佇列,這樣的話每個服務透過更少量的請求(總票數/服務個數),這樣簡單。統一一個隊列又複雜了。

問題7:秒殺後的支付完成,以及未支付取消佔位,如何對剩餘庫存做及時的控制更新?

答案:資料庫裡一個狀態,未支付。如果超過時間,例如45分鐘,庫存會重新會恢復(大家熟知的「回倉」),給我們搶票的啟示是,開動秒殺後,45分鐘之後再試試看,說不定又有票喲~

問題8:不同的使用者瀏覽同一個商品 落在不同的快取實例顯示的庫存完全不一樣 請問老師怎麼做快取資料一致或是允許髒讀?

答:目前的架構設計,請求落在不同的網站上,資料可能不一致(頁面快取不一樣),這個業務場景能接受。但資料庫層面真實資料是沒問題的。

問題9:就算處於業務把優化考慮「3k張火車票,只透3k個下單請求去db」那這3K個訂單就不會發生擁堵了嗎?

答:(1)資料庫抗3k個寫請求還是ok的;(2)可以資料拆分;(3)如果3k扛不住,服務層(web伺服器)可以控制透過去的並發數量,根據壓測情況來吧,3k只是舉例;

問題10;如果在服務端或服務層(web伺服器)處理後台失敗的話,需不需要考慮對這批次失敗的請求做重播?還是就直接丟棄?

答:別重播了,回傳使用者查詢失敗或下單失敗吧,架構設計原則之一是「fail fast」。

問題11.對於大型系統的秒殺,例如12306,同時進行的秒殺活動很多,如何分流?

答案:垂直拆分

問題12、額外又想到一個問題。這套流程做成同步還是非同步的?如果是同步的話,應該還存在會有回應回饋慢的情況。但如果是異步的話,如何控制能夠將回應結果傳回正確的請求方?

答:使用者層面肯定是同步的(使用者的http請求是夯住的),服務層(web伺服器)面可以同步可以非同步。

問題13、秒殺群問題:減庫存是在那個階段減呢?如果是下單鎖庫存的話,大量惡意用戶下單鎖庫存而不支付如何處理呢?

答:資料庫層面寫請求量很低,還好,下單不支付,等時間過完再“回倉”,之前提過了。

技巧:值的我們注意的地方

1、脫離原始網站部署(秒殺功能用的伺服器和商城伺服器不要放在同一個伺服器,防止秒殺崩咯,商城也不能訪問...)

2、多監控、注意監控、找個人盯著看

秒殺的關鍵點:

1、高可用:雙活

2、高並發:負載平衡、安全過濾

設計想法:

1、靜態頁面:cdn(使用各大廠商現成的)、網址隱藏、頁面壓縮、快取機制

2、動態頁面:排隊、非同步、資格搶購

其他建議:

##1、百度的建議:opcode快取、cdn 、更大的伺服器實例

2、阿里的建議:雲端監控、雲端盾牌、ecs、oss、rds、cdn

cdn的使用思路:

##1、靜態資源(圖片、js、css等)上傳到cdn

2、缺點:但要注意,更新時cdn更新不及時,這時就要推了

#1環境、形式:

1、使用者:超大量、正常/壞人(cdn加速也是一種分流,因為他就近訪問cdn的節點)

2、地理:全國各地(延遲1、2s也不行,因為延遲1s可能秒殺就結束了,需要cdn讓用戶就近選擇節點)

3、業務流程:前台商品展示、登記。後台資料存取、資料處理

在秒殺前加個頁面,可以分流、推廣其他商品

##商品展示層架構

頁面的3個狀態:

1、商品展示:倒數計時頁

#2、秒殺進行中:點選進入秒殺頁面

3、秒殺活動結束:提示活動已結束

新想法:以時間線來思考問題

假設我們看見的就是秒殺進行中,往前推就是倒數計時,往後推就是結束了

下圖是從使用者的角度看問題:

## :

靜態資源,存到oss裡部署

#先刪除倒數頁面,馬上給秒殺頁面複製過來

然後用coretab定時執行這個腳本

#總結:

從倒數計時到搶購中:是用linux的定時任務和shell腳本來做

搶購中倒搶購結束:php來做,查表沒了就結束

用戶登錄層架構

#程式碼:

#$.cookie的封裝

資料存取層

 

程式碼:

 

第2層到第3層的時候要怎麼計算? ?

第2層發送資料到第3層網路傳輸dns解析等因素總共造成的延遲大概是多久,這樣就可以評估,需要配置伺服器的數量

#總結為2句話:

1、在關鍵點上不通過,直接回到上一層(使用者登記層)

2、在關鍵點通過,就會通知其他層

效驗:類似微軟序號的加密解密

佇列:使用redis的有序集合

上限:技術標誌位元

資料處理層

#

#

以上是分享php秒殺功能實現的思路的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:csdn。如有侵權,請聯絡admin@php.cn刪除
PHP與Python:了解差異PHP與Python:了解差異Apr 11, 2025 am 12:15 AM

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

php:死亡還是簡單地適應?php:死亡還是簡單地適應?Apr 11, 2025 am 12:13 AM

PHP不是在消亡,而是在不斷適應和進化。 1)PHP從1994年起經歷多次版本迭代,適應新技術趨勢。 2)目前廣泛應用於電子商務、內容管理系統等領域。 3)PHP8引入JIT編譯器等功能,提升性能和現代化。 4)使用OPcache和遵循PSR-12標準可優化性能和代碼質量。

PHP的未來:改編和創新PHP的未來:改編和創新Apr 11, 2025 am 12:01 AM

PHP的未來將通過適應新技術趨勢和引入創新特性來實現:1)適應云計算、容器化和微服務架構,支持Docker和Kubernetes;2)引入JIT編譯器和枚舉類型,提升性能和數據處理效率;3)持續優化性能和推廣最佳實踐。

您什麼時候使用特質與PHP中的抽像類或接口?您什麼時候使用特質與PHP中的抽像類或接口?Apr 10, 2025 am 09:39 AM

在PHP中,trait適用於需要方法復用但不適合使用繼承的情況。 1)trait允許在類中復用方法,避免多重繼承複雜性。 2)使用trait時需注意方法衝突,可通過insteadof和as關鍵字解決。 3)應避免過度使用trait,保持其單一職責,以優化性能和提高代碼可維護性。

什麼是依賴性注入容器(DIC),為什麼在PHP中使用一個?什麼是依賴性注入容器(DIC),為什麼在PHP中使用一個?Apr 10, 2025 am 09:38 AM

依賴注入容器(DIC)是一種管理和提供對象依賴關係的工具,用於PHP項目中。 DIC的主要好處包括:1.解耦,使組件獨立,代碼易維護和測試;2.靈活性,易替換或修改依賴關係;3.可測試性,方便注入mock對象進行單元測試。

與常規PHP陣列相比,解釋SPL SplfixedArray及其性能特徵。與常規PHP陣列相比,解釋SPL SplfixedArray及其性能特徵。Apr 10, 2025 am 09:37 AM

SplFixedArray在PHP中是一種固定大小的數組,適用於需要高性能和低內存使用量的場景。 1)它在創建時需指定大小,避免動態調整帶來的開銷。 2)基於C語言數組,直接操作內存,訪問速度快。 3)適合大規模數據處理和內存敏感環境,但需謹慎使用,因其大小固定。

PHP如何安全地上載文件?PHP如何安全地上載文件?Apr 10, 2025 am 09:37 AM

PHP通過$\_FILES變量處理文件上傳,確保安全性的方法包括:1.檢查上傳錯誤,2.驗證文件類型和大小,3.防止文件覆蓋,4.移動文件到永久存儲位置。

什麼是無效的合併操作員(??)和無效分配運算符(?? =)?什麼是無效的合併操作員(??)和無效分配運算符(?? =)?Apr 10, 2025 am 09:33 AM

JavaScript中處理空值可以使用NullCoalescingOperator(??)和NullCoalescingAssignmentOperator(??=)。 1.??返回第一個非null或非undefined的操作數。 2.??=將變量賦值為右操作數的值,但前提是該變量為null或undefined。這些操作符簡化了代碼邏輯,提高了可讀性和性能。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用