溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

php秒殺功能是如何實(shí)現(xiàn)的

發(fā)布時間:2020-09-30 15:55:00 來源:億速云 閱讀:678 作者:小新 欄目:編程語言

這篇文章主要介紹了php秒殺功能是如何實(shí)現(xiàn)的,具有一定借鑒價值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。

一、秒殺業(yè)務(wù)為什么難做

1)im系統(tǒng),例如qq或者微博,每個人都讀自己的數(shù)據(jù)(好友列表、群列表、個人信息);

2)微博系統(tǒng),每個人讀你關(guān)注的人的數(shù)據(jù),一個人讀多個人的數(shù)據(jù);

3)秒殺系統(tǒng),庫存只有一份,所有人會在集中的時間讀和寫這些數(shù)據(jù),多個人讀一個數(shù)據(jù)。

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

又例如:12306搶票,票是有限的,庫存一份,瞬時流量非常多,都讀相同的庫存。讀寫沖突,鎖非常嚴(yán)重,這是秒殺業(yè)務(wù)難的地方。那我們怎么優(yōu)化秒殺業(yè)務(wù)的架構(gòu)呢?

二、優(yōu)化方向

優(yōu)化方向有兩個:

(1)將請求盡量攔截在系統(tǒng)上游(不要讓鎖沖突落到數(shù)據(jù)庫上去)。傳統(tǒng)秒殺系統(tǒng)之所以掛,請求都壓倒了后端數(shù)據(jù)層,數(shù)據(jù)讀寫鎖沖突嚴(yán)重,并發(fā)高響應(yīng)慢,幾乎所有請求都超時,流量雖大,下單成功的有效流量甚小。以12306為例,一趟火車其實(shí)只有2000張票,200w個人來買,基本沒有人能買成功,請求有效率為0。

(2)充分利用緩存,秒殺買票,這是一個典型的讀多些少的應(yīng)用場景,大部分請求是車次查詢,票查詢,下單和支付才是寫請求。一趟火車其實(shí)只有2000張票,200w個人來買,最多2000個人下單成功,其他人都是查詢庫存,寫比例只有0.1%,讀比例占99.9%,非常適合使用緩存來優(yōu)化。好,后續(xù)講講怎么個“將請求盡量攔截在系統(tǒng)上游”法,以及怎么個“緩存”法,講講細(xì)節(jié)。

三、常見秒殺架構(gòu)

常見的站點(diǎn)架構(gòu)基本是這樣的(絕對不畫忽悠類的架構(gòu)圖)

php秒殺功能是如何實(shí)現(xiàn)的
(1)瀏覽器端,最上層,會執(zhí)行到一些JS代碼

(2)服務(wù)端,這一層會訪問后端數(shù)據(jù),拼html頁面返回給瀏覽器

(3)服務(wù)層(web服務(wù)器),向上游屏蔽底層數(shù)據(jù)細(xì)節(jié),提供數(shù)據(jù)訪問

(4)數(shù)據(jù)層,最終的庫存是存在這里的,mysql是一個典型(當(dāng)然還有會緩存)

這個圖雖然簡單,但能形象的說明大流量高并發(fā)的秒殺業(yè)務(wù)架構(gòu),大家要記得這一張圖。

后面細(xì)細(xì)解析各個層級怎么優(yōu)化。

四、各層次優(yōu)化細(xì)節(jié)

第一層,客戶端怎么優(yōu)化(瀏覽器層,APP層)

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

(a)產(chǎn)品層面,用戶點(diǎn)擊“查詢”或者“購票”后,按鈕置灰,禁止用戶重復(fù)提交請求;

(b)JS層面,限制用戶在x秒之內(nèi)只能提交一次請求;

APP層面,可以做類似的事情,雖然你瘋狂的在搖微信,其實(shí)x秒才向后端發(fā)起一次請求。這就是所謂的“將請求盡量攔截在系統(tǒng)上游”,越上游越好,瀏覽器層,APP層就給攔住,這樣就能擋住80%+的請求,這種辦法只能攔住普通用戶(但99%的用戶是普通用戶)對于群內(nèi)的高端程序員是攔不住的。firebug一抓包,http長啥樣都知道,js是萬萬攔不住程序員寫for循環(huán),調(diào)用http接口的,這部分請求怎么處理?

第二層,服務(wù)端層面的請求攔截

怎么攔截?怎么防止程序員寫for循環(huán)調(diào)用,有去重依據(jù)么?ip?cookie-id?…想復(fù)雜了,這類業(yè)務(wù)都需要登錄,用uid即可。在服務(wù)端層面,對uid進(jìn)行請求計數(shù)和去重,甚至不需要統(tǒng)一存儲計數(shù),直接服務(wù)端內(nèi)存存儲(這樣計數(shù)會不準(zhǔn),但最簡單)。一個uid,5秒只準(zhǔn)透過1個請求,這樣又能攔住99%的for循環(huán)請求。

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

頁面緩存不一定要保證所有服務(wù)端返回一致的頁面,直接放在每個站點(diǎn)的內(nèi)存也是可以的。優(yōu)點(diǎn)是簡單,壞處是http請求落到不同的服務(wù)端,返回的車票數(shù)據(jù)可能不一樣,這是服務(wù)端的請求攔截與緩存優(yōu)化。

好,這個方式攔住了寫for循環(huán)發(fā)http請求的程序員,有些高端程序員(黑客)控制了10w個肉雞,手里有10w個uid,同時發(fā)請求(先不考慮實(shí)名制的問題,小米搶手機(jī)不需要實(shí)名制),這下怎么辦,服務(wù)端按照uid限流攔不住了。

第三層 服務(wù)層來攔截(反正就是不要讓請求落到數(shù)據(jù)庫上去)

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

對于寫請求,做請求隊列,每次只透有限的寫請求去數(shù)據(jù)層(下訂單,支付這樣的寫業(yè)務(wù))

1w部手機(jī),只透1w個下單請求去db

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

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

對于讀請求,怎么優(yōu)化?cache抗,不管是memcached還是redis,單機(jī)抗個每秒10w應(yīng)該都是沒什么問題的。如此限流,只有非常少的寫請求,和非常少的讀緩存mis的請求會透到數(shù)據(jù)層去,又有99.9%的請求被攔住了。

當(dāng)然,還有業(yè)務(wù)規(guī)則上的一些優(yōu)化?;叵?2306所做的,分時分段售票,原來統(tǒng)一10點(diǎn)賣票,現(xiàn)在8點(diǎn),8點(diǎn)半,9點(diǎn),...每隔半個小時放出一批:將流量攤勻。

其次,數(shù)據(jù)粒度的優(yōu)化:你去購票,對于余票查詢這個業(yè)務(wù),票剩了58張,還是26張,你真的關(guān)注么,其實(shí)我們只關(guān)心有票和無票?流量大的時候,做一個粗粒度的“有票”“無票”緩存即可。

第三,一些業(yè)務(wù)邏輯的異步:例如下單業(yè)務(wù)與 支付業(yè)務(wù)的分離。這些優(yōu)化都是結(jié)合 業(yè)務(wù) 來的,我之前分享過一個觀點(diǎn)“一切脫離業(yè)務(wù)的架構(gòu)設(shè)計都是耍流氓”架構(gòu)的優(yōu)化也要針對業(yè)務(wù)。

好了,最后是數(shù)據(jù)庫層

瀏覽器攔截了80%,服務(wù)端攔截了99.9%并做了頁面緩存,服務(wù)層又做了寫請求隊列與數(shù)據(jù)緩存,每次透到數(shù)據(jù)庫層的請求都是可控的。db基本就沒什么壓力了,閑庭信步,單機(jī)也能扛得住,還是那句話,庫存是有限的,小米的產(chǎn)能有限,透這么多請求來數(shù)據(jù)庫沒有意義。

全部透到數(shù)據(jù)庫,100w個下單,0個成功,請求有效率0%。透3k個到數(shù)據(jù),全部成功,請求有效率100%。

五、總結(jié)

上文應(yīng)該描述的非常清楚了,沒什么總結(jié)了,對于秒殺系統(tǒng),再次重復(fù)下我個人經(jīng)驗的兩個架構(gòu)優(yōu)化思路:

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

(2)讀多寫少的常用多使用緩存(緩存抗讀壓力);

瀏覽器和APP:做限速

服務(wù)端:按照uid做限速,做頁面緩存

服務(wù)層(web服務(wù)器):按照業(yè)務(wù)做寫請求隊列控制流量,做數(shù)據(jù)緩存

數(shù)據(jù)層:閑庭信步

并且:結(jié)合業(yè)務(wù)做優(yōu)化

六、Q&A

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

答:每秒鐘的并發(fā)可能沒有1kw,假設(shè)有1kw,解決方案2個:

(1)服務(wù)層(web服務(wù)器)是可以通過加機(jī)器擴(kuò)容的,最不濟(jì)1k臺機(jī)器來唄。

(2)如果機(jī)器不夠,拋棄請求,拋棄50%(50%直接返回稍后再試),原則是要保護(hù)系統(tǒng),不能讓所有用戶都失敗。

問題2、“控制了10w個肉雞,手里有10w個uid,同時發(fā)請求” 這個問題怎么解決哈?

答:上面說了,服務(wù)層(web服務(wù)器)寫請求隊列控制

問題3:限制訪問頻次的緩存,是否也可以用于搜索?例如A用戶搜索了“手機(jī)”,B用戶搜索“手機(jī)”,優(yōu)先使用A搜索后生成的緩存頁面?

答:這個是可以的,這個方法也經(jīng)常用在“動態(tài)”運(yùn)營活動頁,例如短時間推送4kw用戶app-push運(yùn)營活動,做頁面緩存。

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

答:處理失敗返回下單失敗,讓用戶再試。隊列成本很低,爆了很難吧。最壞的情況下,緩存了若干請求之后,后續(xù)請求都直接返回“無票”(隊列里已經(jīng)有100w請求了,都等著,再接受請求也沒有意義了)

問題5:服務(wù)端過濾的話,是把uid請求數(shù)單獨(dú)保存到各個站點(diǎn)的內(nèi)存中么?如果是這樣的話,怎么處理多臺服務(wù)器集群經(jīng)過負(fù)載均衡器將相同用戶的響應(yīng)分布到不同服務(wù)器的情況呢?還是說將服務(wù)端的過濾放到負(fù)載均衡前?

答:可以放在內(nèi)存,這樣的話看似一臺服務(wù)器限制了5s一個請求,全局來說(假設(shè)有10臺機(jī)器),其實(shí)是限制了5s 10個請求,解決辦法:

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

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

問題6:服務(wù)層(web服務(wù)器)過濾的話,隊列是服務(wù)層(web服務(wù)器)統(tǒng)一的一個隊列?還是每個提供服務(wù)的服務(wù)器各一個隊列?如果是統(tǒng)一的一個隊列的話,需不需要在各個服務(wù)器提交的請求入隊列前進(jìn)行鎖控制?

答:可以不用統(tǒng)一一個隊列,這樣的話每個服務(wù)透過更少量的請求(總票數(shù)/服務(wù)個數(shù)),這樣簡單。統(tǒng)一一個隊列又復(fù)雜了。

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

答:數(shù)據(jù)庫里一個狀態(tài),未支付。如果超過時間,例如45分鐘,庫存會重新會恢復(fù)(大家熟知的“回倉”),給我們搶票的啟示是,開動秒殺后,45分鐘之后再試試看,說不定又有票喲~

問題8:不同的用戶瀏覽同一個商品 落在不同的緩存實(shí)例顯示的庫存完全不一樣 請問老師怎么做緩存數(shù)據(jù)一致或者是允許臟讀?

答:目前的架構(gòu)設(shè)計,請求落到不同的站點(diǎn)上,數(shù)據(jù)可能不一致(頁面緩存不一樣),這個業(yè)務(wù)場景能接受。但數(shù)據(jù)庫層面真實(shí)數(shù)據(jù)是沒問題的。

問題9:就算處于業(yè)務(wù)把優(yōu)化考慮“3k張火車票,只透3k個下單請求去db”那這3K個訂單就不會發(fā)生擁堵了嗎?

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

問題10;如果在服務(wù)端或者服務(wù)層(web服務(wù)器)處理后臺失敗的話,需不需要考慮對這批處理失敗的請求做重放?還是就直接丟棄?

答:別重放了,返回用戶查詢失敗或者下單失敗吧,架構(gòu)設(shè)計原則之一是“fail fast”。

問題11.對于大型系統(tǒng)的秒殺,比如12306,同時進(jìn)行的秒殺活動很多,如何分流?

答:垂直拆分

問題12、額外又想到一個問題。這套流程做成同步還是異步的?如果是同步的話,應(yīng)該還存在會有響應(yīng)反饋慢的情況。但如果是異步的話,如何控制能夠?qū)㈨憫?yīng)結(jié)果返回正確的請求方?

答:用戶層面肯定是同步的(用戶的http請求是夯住的),服務(wù)層(web服務(wù)器)面可以同步可以異步。

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

答:數(shù)據(jù)庫層面寫請求量很低,還好,下單不支付,等時間過完再“回倉”,之前提過了。

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

1、脫離原站點(diǎn)部署(秒殺功能用的服務(wù)器和商城服務(wù)器不要放在同一個服務(wù)器,防止秒殺崩咯,商城也不能訪問...)

2、多監(jiān)控、注意監(jiān)控、找個人盯著看

秒殺的關(guān)鍵點(diǎn):

1、高可用:雙活

2、高并發(fā):負(fù)載均衡、安全過濾

設(shè)計思路:

1、靜態(tài)頁面:cdn(使用各大廠商現(xiàn)成的)、網(wǎng)址隱藏、頁面壓縮、緩存機(jī)制

2、動態(tài)頁面:排隊、異步、資質(zhì)搶購

其他建議:

1、百度的建議:opcode緩存、cdn、更大的服務(wù)器實(shí)例

2、阿里的建議:云監(jiān)控、云盾、ecs、oss、rds、cdn

cdn的使用思路:

1、將靜態(tài)資源(圖片、js、css等)上傳到cdn

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

認(rèn)清當(dāng)前環(huán)境、形式:

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

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

3、業(yè)務(wù)流程:前臺商品展示、登記。后臺數(shù)據(jù)接入、數(shù)據(jù)處理

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

php秒殺功能是如何實(shí)現(xiàn)的

商品展示層架構(gòu)

頁面的3個狀態(tài):

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

2、秒殺進(jìn)行中:點(diǎn)擊進(jìn)入秒殺頁面

3、秒殺活動結(jié)束:提示活動已結(jié)束

新思路:以時間線來思考問題

假設(shè)我們看見的就是秒殺進(jìn)行中,往前推就是倒計時,往后推就是結(jié)束了

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

php秒殺功能是如何實(shí)現(xiàn)的

代碼:

php秒殺功能是如何實(shí)現(xiàn)的

靜態(tài)資源,存到oss里部署

php秒殺功能是如何實(shí)現(xiàn)的

先刪除倒計時頁面,馬上給秒殺中頁面復(fù)制過來

然后用coretab定時執(zhí)行這個腳本

php秒殺功能是如何實(shí)現(xiàn)的

總結(jié):

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

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

用戶登記層架構(gòu)

php秒殺功能是如何實(shí)現(xiàn)的

php秒殺功能是如何實(shí)現(xiàn)的

代碼:

php秒殺功能是如何實(shí)現(xiàn)的

php秒殺功能是如何實(shí)現(xiàn)的

$.cookie的封裝

php秒殺功能是如何實(shí)現(xiàn)的

數(shù)據(jù)接入層

php秒殺功能是如何實(shí)現(xiàn)的

php秒殺功能是如何實(shí)現(xiàn)的

代碼:

php秒殺功能是如何實(shí)現(xiàn)的

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

第2層發(fā)送數(shù)據(jù)到第3層 + 網(wǎng)絡(luò)傳輸dns解析等因素總共造成的延時大概是多長時間,這樣就可以評估,需要配置服務(wù)器的數(shù)量

php秒殺功能是如何實(shí)現(xiàn)的

總結(jié)為2句話:

1、在關(guān)鍵點(diǎn)上不通過,直接返回上一層(用戶登記層)

2、在關(guān)鍵點(diǎn)通過,就會通知其他層

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

隊列:使用redis的有序集合

上限:技術(shù)標(biāo)志位

數(shù)據(jù)處理層

php秒殺功能是如何實(shí)現(xiàn)的

php秒殺功能是如何實(shí)現(xiàn)的

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享php秒殺功能是如何實(shí)現(xiàn)的內(nèi)容對大家有幫助,同時也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,遇到問題就找億速云,詳細(xì)的解決方法等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI