1. 程式人生 > >高併發秒殺系統架構設計 · 搶購、微信紅包、一元奪寶

高併發秒殺系統架構設計 · 搶購、微信紅包、一元奪寶



秒殺業務在各業務中已然非常流行,這裡我將網際網路行業中的秒殺定義為:在非常短的時間內,將一件商品分成多份進行購買的行為。微信搶紅包、一元奪寶、雙11大促搶購等業務本質上都可視作秒殺業務。而最近大熱的搶紅包的難度在於這是和錢打交道的秒殺場景,對於事務的要求性更高。秒殺業務的難點或者說痛點在於:同一件商品在同一時間段內有非常多的使用者去進行搶奪,從而造成伺服器資源的緊張。

非秒殺情況下,比如非大促的時候,使用者購買的體驗都是非常不錯的。但是在秒殺場景下,這時意味著多個使用者在同時搶一件商品,也就是併發很高,但集中在同一商品上,造成實質為序列操作。因為在資料庫這層本質執行的是對同一件商品扣庫存:

更糟糕的是,無論是MySQL、Oracle、還是其他關係型資料庫,這會造成大量無意義的上下文切換,從而導致資源浪費。假設在網易考拉上有10000個使用者對skuId=1的這件商品進行搶購,那麼每個時間點僅有一個使用者可以獲得進入資料庫操作的許可權,剩下的9999個使用者需要等待,待前面的使用者完成操作後,會喚醒9999個使用者,告訴他們現在可以進入了,9999個使用者重新爭奪一次,最終又僅有一個使用者進入,這就是所謂的上下文切換。在秒殺場景下,這個代價將會非常大,一個顯著的表現是CPU負載非常高,但資料庫請求的負載卻又非常低。

秒殺常見的三種業務型別為:大促搶購、搶微信/易信紅包、一元奪寶。從併發量來看,大促搶購 > 

微信紅包 > 一元奪寶。從可靠性要求來看:微信紅包 > 一元奪寶 > 大促搶購。但是無論是哪一種,原則上都不能超售,一旦超售後果非常嚴重,特別是微信紅包業務。因此,我個人非常不建議將秒殺業務放在快取中設計的架構,這是在賭RP,後果卻可能非常嚴重。

秒殺業務的架構設計其實並不難,簡單來說,就是不要讓資料庫處理承擔這麼多請求,減少無謂的上下文切換,業界一個比較學術的稱謂叫做:限流。

秒殺優化——限流

秒殺架構設計

我傾向於將秒殺的系統架構設計分為以下幾層:

  • 客戶端層:使用者發起秒殺的瀏覽器、app或其他客戶端;

  • 前端Web展示層,負責接收使用者請求,通常是Nginx或Apache等Web伺服器;

  • 服務介面呼叫層,接收到請求,呼叫相關服務進行秒殺操作;

  • 資料儲存層,對於秒殺操作進行持久化。要對秒殺進行優化,則對架構設計上需要對這三層進行限流。

客戶端層優化

客戶端層的優化比較簡單明瞭,原則上來說依然是限制使用者發秒秒殺的次數,從而做到限流的效果。比如在秒殺發起後,按鈕變灰。這類做法和簡訊驗證碼一樣,簡訊傳送後一般需要等待120秒才能再次接收驗證碼,120秒內的傳送簡訊驗證碼是灰色的。然而這種做法對於高階程式設計師來說,就沒啥用了。因為Chrome、Firefox firebug按F12進入開發者模式就能知道具體的請求。只要有心,模擬類似請求,搶幾個月餅的難度真不大。

前端展示層優化

在大並的秒殺髮量訪問場景下,前端展示也要做好相應的頁面級快取,比如10秒內同一使用者的頁面快取,同一商品的頁面快取。更重要的是,這樣能大大提升使用者的體驗。當然,現在瀏覽器本身也會快取一部分資料,從而提升使用者的訪問的體驗。當然,這也是有利有弊。

服務介面呼叫優化

對於618、雙11這樣的全民搶購應用場景,做好前兩塊優化是遠不夠的。上述這些優化其實都可以作為例行的開發規範。但是在大促時間段,就是會有超大規模的訪問請求,比如幾萬個人同時搶小米手機,而在開始前是可以知道庫存的。因此,開發人員可以通過訊息佇列或者快取CAS機制來限制訪問到資料庫層實際的數量。而對於超出庫存的,則前端可以返回等待中,定期再進行重試,直到庫存為0為止。

在這裡還有個小問題,那就是有些使用者可能已經搶到秒殺資格,但是最後沒有完成付款。這在紅包業務中不存在,但是在電商行業中卻是有可能的。淘寶在高峰時間的處理方法是訂單30分鐘未完成支付即將關閉訂單,庫存重新可見。而對於像一元奪寶這樣的業務,30分鐘時間顯得有些太長了。故一元奪寶支付失敗並沒有重試訂單的機制。

資料庫層的優化

服務介面的呼叫能起到限流的作用,但是是對同一件商品進行限流。大促期間訪問到資料庫這裡的請求依然不容忽略。如果資料庫這層有2000個使用者在同時進行秒殺操作,那麼這個開銷依然非常巨大。這時強烈建議使用者開啟資料庫執行緒池功能(注意:不是連線池),比如MySQL企業版的Thread Pool外掛、社群版的Percona、InnoSQL資料庫都支援執行緒池。

執行緒池的機制是每個使用者的連線並不是一定會產生一個實際的硬連線,而是通過Pool機制從中進行分配,也就是實際在資料庫內部執行的執行緒數是固定的,減小上下文切換的時間,從而大幅提升資料庫的效能。具體可見我之前分享過的文章:秒殺應用的MySQL資料庫優化,就是這麼簡單

資料庫架構設計

資料庫表結構設計與應用

表結構設計其實大同小異,這裡以微信紅包業務作為案例分析:

分散式資料庫架構

即使做了上述這麼多秒殺優化,相信對於高峰期的微信搶紅包業務來說也是無法承載的。記得有同學在IMG微信群中有說過(1年多前),微信紅包是由70臺伺服器組成的分散式資料庫叢集。對於這樣的分散式叢集,開發人員可以選擇紅包Id作為均衡欄位進行分庫分表,通過分散式資料庫的可擴充套件性提升整個叢集的效能。需要特別注意的是,由於是分散式架構,建議將上述紅包Id的資料型別更改為全域性唯一的字串型別,使用者可以自己生成一個規則,或直接使用UUID這樣的函式。

至於分散式資料庫中介軟體的選擇,網易十年技術積累的分散式中介軟體DDB現已商用了,網易全程提供技術支援。歡迎微信諮詢:82946772。BTW,想要加入IMG微信群的同學,也可以加我微信獲得微信群邀請。

秒殺業務在各業務中已然非常流行,這裡我將網際網路行業中的秒殺定義為:在非常短的時間內,將一件商品分成多份進行購買的行為。微信搶紅包、一元奪寶、雙11大促搶購等業務本質上都可視作秒殺業務。而最近大熱的搶紅包的難度在於這是和錢打交道的秒殺場景,對於事務的要求性更高。秒殺業務的難點或者說痛點在於:同一件商品在同一時間段內有非常多的使用者去進行搶奪,從而造成伺服器資源的緊張。

非秒殺情況下,比如非大促的時候,使用者購買的體驗都是非常不錯的。但是在秒殺場景下,這時意味著多個使用者在同時搶一件商品,也就是併發很高,但集中在同一商品上,造成實質為序列操作。因為在資料庫這層本質執行的是對同一件商品扣庫存:

更糟糕的是,無論是MySQL、Oracle、還是其他關係型資料庫,這會造成大量無意義的上下文切換,從而導致資源浪費。假設在網易考拉上有10000個使用者對skuId=1的這件商品進行搶購,那麼每個時間點僅有一個使用者可以獲得進入資料庫操作的許可權,剩下的9999個使用者需要等待,待前面的使用者完成操作後,會喚醒9999個使用者,告訴他們現在可以進入了,9999個使用者重新爭奪一次,最終又僅有一個使用者進入,這就是所謂的上下文切換。在秒殺場景下,這個代價將會非常大,一個顯著的表現是CPU負載非常高,但資料庫請求的負載卻又非常低。

秒殺常見的三種業務型別為:大促搶購、搶微信/易信紅包、一元奪寶。從併發量來看,大促搶購 > 微信紅包 > 一元奪寶。從可靠性要求來看:微信紅包 > 一元奪寶 > 大促搶購。但是無論是哪一種,原則上都不能超售,一旦超售後果非常嚴重,特別是微信紅包業務。因此,我個人非常不建議將秒殺業務放在快取中設計的架構,這是在賭RP,後果卻可能非常嚴重。

秒殺業務的架構設計其實並不難,簡單來說,就是不要讓資料庫處理承擔這麼多請求,減少無謂的上下文切換,業界一個比較學術的稱謂叫做:限流。

秒殺優化——限流

秒殺架構設計

我傾向於將秒殺的系統架構設計分為以下幾層:

  • 客戶端層:使用者發起秒殺的瀏覽器、app或其他客戶端;

  • 前端Web展示層,負責接收使用者請求,通常是Nginx或Apache等Web伺服器;

  • 服務介面呼叫層,接收到請求,呼叫相關服務進行秒殺操作;

  • 資料儲存層,對於秒殺操作進行持久化。要對秒殺進行優化,則對架構設計上需要對這三層進行限流。

客戶端層優化

客戶端層的優化比較簡單明瞭,原則上來說依然是限制使用者發秒秒殺的次數,從而做到限流的效果。比如在秒殺發起後,按鈕變灰。這類做法和簡訊驗證碼一樣,簡訊傳送後一般需要等待120秒才能再次接收驗證碼,120秒內的傳送簡訊驗證碼是灰色的。然而這種做法對於高階程式設計師來說,就沒啥用了。因為Chrome、Firefox firebug按F12進入開發者模式就能知道具體的請求。只要有心,模擬類似請求,搶幾個月餅的難度真不大。

前端展示層優化

在大並的秒殺髮量訪問場景下,前端展示也要做好相應的頁面級快取,比如10秒內同一使用者的頁面快取,同一商品的頁面快取。更重要的是,這樣能大大提升使用者的體驗。當然,現在瀏覽器本身也會快取一部分資料,從而提升使用者的訪問的體驗。當然,這也是有利有弊。

服務介面呼叫優化

對於618、雙11這樣的全民搶購應用場景,做好前兩塊優化是遠不夠的。上述這些優化其實都可以作為例行的開發規範。但是在大促時間段,就是會有超大規模的訪問請求,比如幾萬個人同時搶小米手機,而在開始前是可以知道庫存的。因此,開發人員可以通過訊息佇列或者快取CAS機制來限制訪問到資料庫層實際的數量。而對於超出庫存的,則前端可以返回等待中,定期再進行重試,直到庫存為0為止。

在這裡還有個小問題,那就是有些使用者可能已經搶到秒殺資格,但是最後沒有完成付款。這在紅包業務中不存在,但是在電商行業中卻是有可能的。淘寶在高峰時間的處理方法是訂單30分鐘未完成支付即將關閉訂單,庫存重新可見。而對於像一元奪寶這樣的業務,30分鐘時間顯得有些太長了。故一元奪寶支付失敗並沒有重試訂單的機制。

資料庫層的優化

服務介面的呼叫能起到限流的作用,但是是對同一件商品進行限流。大促期間訪問到資料庫這裡的請求依然不容忽略。如果資料庫這層有2000個使用者在同時進行秒殺操作,那麼這個開銷依然非常巨大。這時強烈建議使用者開啟資料庫執行緒池功能(注意:不是連線池),比如MySQL企業版的Thread Pool外掛、社群版的Percona、InnoSQL資料庫都支援執行緒池。

執行緒池的機制是每個使用者的連線並不是一定會產生一個實際的硬連線,而是通過Pool機制從中進行分配,也就是實際在資料庫內部執行的執行緒數是固定的,減小上下文切換的時間,從而大幅提升資料庫的效能。具體可見我之前分享過的文章:秒殺應用的MySQL資料庫優化,就是這麼簡單

資料庫架構設計

資料庫表結構設計與應用

表結構設計其實大同小異,這裡以微信紅包業務作為案例分析:

分散式資料庫架構

即使做了上述這麼多秒殺優化,相信對於高峰期的微信搶紅包業務來說也是無法承載的。記得有同學在IMG微信群中有說過(1年多前),微信紅包是由70臺伺服器組成的分散式資料庫叢集。對於這樣的分散式叢集,開發人員可以選擇紅包Id作為均衡欄位進行分庫分表,通過分散式資料庫的可擴充套件性提升整個叢集的效能。需要特別注意的是,由於是分散式架構,建議將上述紅包Id的資料型別更改為全域性唯一的字串型別,使用者可以自己生成一個規則,或直接使用UUID這樣的函式。

至於分散式資料庫中介軟體的選擇,網易十年技術積累的分散式中介軟體DDB現已商用了,網易全程提供技術支援。歡迎微信諮詢:82946772。BTW,想要加入IMG微信群的同學,也可以加我微信獲得微信群邀請。