高并發秒殺系統架構設計
秒殺業務與難點
秒殺業務在各業務中已然非常流行,這里我將互聯網行業中的秒殺定義為:在非常短的時間內,將一件商品分成多份進行購買的行為。微信搶紅包、 、雙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、 數據庫都支持線程池。
線程池的機制是每個用戶的連接并不是一定會產生一個實際的硬連接,而是通過Pool機制從中進行分配,也就是實際在數據庫內部運行的線程數是固定的,減小上下文切換的時間,從而大幅提升數據庫的性能。
數據庫架構設計
數據庫表結構設計與應用
表結構設計其實大同小異,這里以微信紅包業務作為案例分析:
分布式數據庫架構
即使做了上述這么多秒殺優化,相信對于高峰期的微信搶紅包業務來說也是無法承載的。記得有同學在IMG微信群中有說過(1年多前),微信紅包是由70臺服務器組成的分布式數據庫集群。對于這樣的分布式集群,開發人員可以選擇紅包Id作為均衡字段進行分庫分表,通過分布式數據庫的可擴展性提升整個集群的性能。需要特別注意的是,由于是分布式架構, 建議將上述紅包Id的數據類型更改為全局唯一的字符串類型 ,用戶可以自己生成一個規則,或直接使用UUID這樣的函數。
至于分布式數據庫中間件的選擇,網易十年技術積累的分布式中間件DDB現已商用了,網易全程提供技術支持。歡迎微信咨詢:82946772。BTW,想要加入IMG微信群的同學,也可以加我微信獲得微信群邀請。
長期堅持原創真的很不容易,多次想放棄。堅持是一種信仰,專注是一種態度!
來自:http://mp.weixin.qq.com/s/NCQNByi4wW7fzrsBqcLp2A