秒殺請求在時間上高度集中於某一特定的時間點。這樣一來,就會致使一個特別高的流量峯值,它對資源的消耗是瞬時的。java
可是秒殺場景,最終能搶到商品的人數是固定的,也就是說100和10000人發起請求結果是同樣的,併發度越高,無效請求也越多。mysql
可是從業務上來講,秒殺活動是但願更多的人來參與的,也就是開始以前但願有更多的人來刷頁面,開始下單時,能夠設計一些規則,讓併發的請求更多地延緩,過濾掉一些無效請求。nginx
服務器的處理資源是恆定的,出現峯值的話,很容易處理不過來,閒的時候卻又沒有什麼要處理。可是爲了保證服務質量,不少處理資源只能按照最忙的時候來預估,而這會致使資源的一個浪費。算法
針對秒殺這一場景,削峯從本質上來講就是更多地延緩用戶請求的發出,聽從「請求數要儘可能少」的原則。sql
削峯的一些操做思路:熔斷算法、答題、分層過濾。數據庫
分層過濾的核心思想是:在不一樣的層次儘量的過濾掉無效請求,讓「漏斗」最末端的纔是有效請求。而要達到這種效果,咱們就必須對數據作分層的校驗。分層校驗的基本原則是:緩存
分層校驗的目的是:在讀系統中,儘可能減小因爲一致性校驗帶來的系統瓶頸,可是儘可能將不影響性能的檢查條件提早,如用戶是否具備秒殺資格、商品狀態是否正常、用戶答題是否正確、秒殺是否已經結束、是否非法請求、營銷等價物是否充足等;在寫數據系統中,主要對寫的數據(如「庫存」)作一致性檢查,最後在數據庫層保證數據的最終準確性。安全
服務端性能通常用QPS(Query Per Second,每秒請求數)來衡量,還有一個影響和QPS也息息相關,那就是響應時間(Response Time ,RT),它能夠理解爲服務器處理響應的耗時。服務器
正常狀況下響應時間(RT)越短,一秒鐘處理的請求數(QPS)天然也就會越多網絡
總QPS = (1000ms /響應時間)* 線程數量,這樣性能就和兩個因素相關,一個是一次響應服務端耗時,一個是處理請求的線程數
對於大部分的Web系統而言,響應時間通常都是由CPU執行時間和線程等待時間(好比RPC、IO等待、Sleep、Wait等)組成,即服務器在處理一個請求時,一部分是CPU自己在作運算,還有一部分是在各類等待。
線程數不是越多越好,由於線程自己也消耗資源。例如線程越多系統的線程切換成本就會越高,並且每一個線程也都會耗費必定內存。
通常默認配置,即「線程數=2*CPU核數+1」,一個根據最佳實踐出來的公式:
線程數=【(線程等待時間+線程CPU時間)/線程CPU時間】*CPU數量
就服務器而言,會出現瓶頸的地方不少,例如CPU、內存、磁盤以及網絡等均可能會致使瓶頸。此外,不一樣的系統對瓶頸的關注度也不同,例如對緩存系統而言,制約它的是內存,而對存儲型系統來講I/O更容易是瓶頸。咱們定位的場景是秒殺,它的瓶頸更多地發生在CPU上
CPU診斷工具能夠發現CPU的消耗,最經常使用的就是JProfiler和Yourkit這兩工具,它們能夠列出整個請求中每一個函數的CPU執行時間,能夠發現那個函數消耗的CPU時間最多,以便你有針對性地作優化。還能夠經過jstack定時地打印調用棧,若是某些函數調用頻繁或者耗時較多,那麼那些函數就會屢次出如今系統調用棧裏,這樣至關於採樣的方式也可以發現耗時較多的函數。
簡單判斷CPU是否是瓶頸?一個辦法就是看當QPS達到極限時,你的服務器的CPU使用率是否是超過了95%,若是沒有超過,那麼表示CPU還有提高的空間,要麼是有鎖限制,要麼是有過多的本地I/O等待發生。
java極致優化
併發讀優化
採用應用層LocalCache,即在秒殺系統的單機上緩存商品相關的數據。須要將數據劃分紅動態數據和靜態數據分別進行處理:
庫存這種頻繁更新的數據,一旦數據不一致,會不會致使超賣?
這就要用到前面介紹的讀數據的分層校驗原則,讀的場景能夠容許必定的髒數據,由於這裏的誤判只會致使少許本來無庫存的下單請求被誤認爲有庫存,能夠等到真正寫數據時再保證最終一致性,經過在數據的高可用性和一致性之間平衡,來解決高併發的數據讀取問題。
總結:首先是發現短板,其次是減小數據,兩個地方特別影響性能,一是服務端在處理數據時不可避免地存在字符到字節的相互轉化,二是HTTP請求時要作Gzip壓縮,還有網絡傳輸的耗時。再次,就是數據分級,也就是要保證首屏爲先、重要信息爲先,次要信息則異步加載,以這種方式提高用戶獲取數據的體驗。最後就是減小中間環節,減小字符到字節的轉換,增長預處理(提早作字符到字節的轉換)去掉不須要的操做。此外,要作好優化,你還需啊作好應用基線,好比性能基線(什麼時候性能忽然降低)、成本基線(活動用了多少臺機器)、鏈路基線(咱們的系統發生了那些變化),你能夠經過這些基線持續關注系統的性能,作到在代碼上提高編碼質量,在業務上改掉不合理的調用,在架構和調用鏈路上不斷的改進。
用戶實際購買流程通常分爲兩步:下單和支付。咱們選擇在哪一個階段減小庫存是個問題
減庫存操做通常有以下幾個方式:
這3種方式都會有一些問題:
針對這種狀況,解決辦法仍是要結合安全和反做弊的措施來制止。例如,給常常下單不付款的賣家進行識別打標、給某些類目設置最大購買件數、以及對重複下單不付款的操做進行次數限制。
秒殺庫存的極致優化:
秒殺商品和普通商品的減庫存仍是有些差別的,例如商品數量比較少,交易時間段也比較短,咱們能夠大膽的用緩存來保存庫存,進行操做。
若是必須使用數據庫減小庫存,因爲mysql在處理同一個數據的時候會有大量線程來競爭InnoDB行鎖,而併發度越高時等待線程會越多,TPS(Transaction Per Second,每秒處理的消息數)會降低,RT(響應時間)會上升,數據庫的吞吐量就會嚴重受影響。
這時候單個熱點商品會影響整個數據庫的性能,致使0.01%的商品影響99.99%的商品的售賣,這是咱們不肯意看到的。一個思路是進行隔離,將熱點商品放到單獨的熱點庫中。可是維護上會比較麻煩,好比要作熱點數據的動態遷移以及單獨的數據庫等。
而分離熱點數據,仍然沒有解決併發鎖的問題,要解決併發鎖的問題,咱們能夠
排隊和鎖競爭都是要等待,可是有區別。mysql的innodb內部的死鎖檢測,以及mysql server和innodb的切換會比較消耗性能。
高可用建設須要考慮到系統建設的各個階段,也就是說它其實貫穿了系統建設的整個生命週期。
針對秒殺系統,咱們在遇到大流量時,應該從哪些方面來保障系統的穩定運行,更多的是看針對運行階段進行處理。