秒殺系統(tǒng)架構(gòu)設(shè)計都有哪些關(guān)鍵點
秒殺其實主要解決兩個問題
- 一個是并發(fā)讀
- 一個是并發(fā)寫;
架構(gòu)上的高可用、一致性和高性能的要求
- 高性能。 秒殺涉及大量的并發(fā)讀和并發(fā)寫,因此支持高并發(fā)訪問這點非常關(guān)鍵。從設(shè)計數(shù)據(jù)的動靜分離方案、熱點的發(fā)現(xiàn)與隔離、請求的削峰與分層過濾、服務(wù)端的極致優(yōu)化這 4 個方面重點介紹。
- 一致性。 秒殺中商品減庫存的實現(xiàn)方式同樣關(guān)鍵??上攵?,有限數(shù)量的商品在同一時刻被很多倍的請求同時來減庫存,減庫存又分為“拍下減庫存”“付款減庫存”以及預(yù)扣等幾種,在大并發(fā)更新的過程中都要保證數(shù)據(jù)的準(zhǔn)確性,其難度可想而知。因此,我將用一篇文章來專門講解如何設(shè)計秒殺減庫存方案。
- 高可用。 雖然我介紹了很多極致的優(yōu)化思路,但現(xiàn)實中總難免出現(xiàn)一些我們考慮不到的情況,所以要保證系統(tǒng)的高可用和正確性,我們還要設(shè)計一個 PlanB 來兜底,以便在最壞情況發(fā)生時仍然能夠從容應(yīng)對。專欄的最后,我將帶你思考可以從哪些環(huán)節(jié)來設(shè)計兜底方案。
設(shè)計秒殺系統(tǒng)時應(yīng)該注意的5個架構(gòu)原則
架構(gòu)原則:“4 要 1 不要”
- 數(shù)據(jù)要盡量少
- 請求數(shù)要盡量少
- 路徑要盡量短
- 依賴要盡量少
- 不要有單點
1w/s 請求量
1.把秒殺系統(tǒng)獨立出來單獨打造一個系統(tǒng),這樣可以有針對性地做優(yōu)化,例如這個獨立出來的系統(tǒng)就減少了店鋪裝修的功能,減少了頁面的復(fù)雜度;
1.在系統(tǒng)部署上也獨立做一個機器集群,這樣秒殺的大流量就不會影響到正常的商品購買集群的機器負(fù)載;
- 將熱點數(shù)據(jù)(如庫存數(shù)據(jù))單獨放到一個緩存系統(tǒng)中,以提高“讀性能”;
- 增加秒殺答題,防止有秒殺器搶單。
100w/s 請求量
- 對頁面進(jìn)行徹底的動靜分離,使得用戶秒殺時不需要刷新整個頁面,而只需要點擊搶寶按鈕,借此把頁面刷新的數(shù)據(jù)降到最少;
- 在服務(wù)端對秒殺商品進(jìn)行本地緩存,不需要再調(diào)用依賴系統(tǒng)的后臺服務(wù)獲取數(shù)據(jù),甚至不需要去公共的緩存集群中查詢數(shù)據(jù),這樣不僅可以減少系統(tǒng)調(diào)用,而且能夠避免壓垮公共緩存集群。
- 增加系統(tǒng)限流保護(hù),防止最壞情況發(fā)生。
1W QPS,10W QPS ,100WQPS在架構(gòu)升級前遇到的性能瓶
- 10w級別可能瓶頸就在數(shù)據(jù)讀取上,通過增加緩存一般就能解決
- 100w那么,可能服務(wù)端的網(wǎng)絡(luò)可能都是瓶頸,所以要把大部分的靜態(tài)數(shù)據(jù)放到cdn上甚至緩存在瀏覽器里
二八原則:有針對性地處理好系統(tǒng)的“熱點數(shù)據(jù)”
具體到“秒殺”業(yè)務(wù),我們可以在以下幾個層次實現(xiàn)隔離
- 業(yè)務(wù)隔離。把秒殺做成一種營銷活動,賣家要參加秒殺這種營銷活動需要單獨報名,從技術(shù)上來說,賣家報名后對我們來說就有了已知熱點,因此可以提前做好預(yù)熱。
- 系統(tǒng)隔離。系統(tǒng)隔離更多的是運行時的隔離,可以通過分組部署的方式和另外 99% 分開。秒殺可以申請單獨的域名,目的也是讓請求落到不同的集群中。
- 數(shù)據(jù)隔離。秒殺所調(diào)用的數(shù)據(jù)大部分都是熱點數(shù)據(jù),比如會啟用單獨的 Cache 集群或者 MySQL 數(shù)據(jù)庫來放熱點數(shù)據(jù),目的也是不想 0.01% 的數(shù)據(jù)有機會影響 99.99% 數(shù)據(jù)。
流量削峰這事應(yīng)該怎么做?
排隊
答題
分層過濾
秒殺系統(tǒng)“減庫存”設(shè)計的核心邏輯
下單減庫存
付款減庫存
預(yù)扣庫存
大型秒殺中如何減庫存?
- “下單減庫存”在數(shù)據(jù)一致性上,主要就是保證大并發(fā)請求時庫存數(shù)據(jù)不能為負(fù)數(shù),也就是要保證數(shù)據(jù)庫中的庫存字段值不能為負(fù)數(shù),一般我們有多種解決方案:一種是在應(yīng)用程序中通過事務(wù)來判斷,即保證減后庫存不能為負(fù)數(shù),否則就回滾;另一種辦法是直接設(shè)置數(shù)據(jù)庫的字段數(shù)據(jù)為無符號整數(shù),這樣減后庫存字段值小于零時會直接執(zhí)行 SQL 語句來報錯;再有一種就是使用 CASE WHEN 判斷語句,例如這樣的 SQL 語句:
UPDATE item SET inventory = CASE WHEN inventory >= xxx THEN inventory-xxx ELSE inventory END
要解決并發(fā)鎖的問題,有兩種辦法:
- 應(yīng)用層做排隊。按照商品維度設(shè)置隊列順序執(zhí)行,這樣能減少同一臺機器對數(shù)據(jù)庫同一行記錄進(jìn)行操作的并發(fā)度,同時也能控制單個商品占用數(shù)據(jù)庫連接的數(shù)量,防止熱點商品占用太多的數(shù)據(jù)庫連接。
- 數(shù)據(jù)庫層做排隊。應(yīng)用層只能做到單機的排隊,但是應(yīng)用機器數(shù)本身很多,這種排隊方式控制并發(fā)的能力仍然有限,所以如果能在數(shù)據(jù)庫層做全局排隊是最理想的。阿里的數(shù)據(jù)庫團(tuán)隊開發(fā)了針對這種 MySQL 的 InnoDB 層上的補丁程序(patch),可以在數(shù)據(jù)庫層上對單行記錄做到并發(fā)排隊。