實現秒殺的幾個想法(續)

轉自:http://blog.itpub.net/29254281/viewspace-1800617/css

仍是秒殺.
秒殺通常有幾個場景
1.電商秒殺商品
2.搶紅包
3.搶票

假設一個場景以下:
    某電商公司搞活動,一折秒殺,推出幾種秒殺的商品,每種商品1000個,預計100w人搶購
要求:
    不能超賣.絕對不能夠賣多了.
    數據庫要扣減庫存,而且記錄訂單明細.

難點分析
1.不能阻塞.
海量的請求就像血栓同樣,遍走周身,一旦遇到瓶頸,就會堵塞整個血管.
因此必定要讓海量的用戶請求,儘快結束.

2.數據庫單行更新
大量的 update 庫存表 set 剩餘數量=剩餘數量-1 where 商品ID=?
這種單行更新,有行鎖,會阻塞其餘事務,佔用寶貴的數據庫處理能力.

針對這種場景,綜合了不少資料
我以爲能夠嘗試幾個關於秒殺的優化.



1.Web服務器集羣層,卸載流量
    海量的用戶秒殺請求,本質上是一個排序,先到先得.
    可是如此之多的請求,徹底響應,難度又很大.
    因此在Web服務器集羣,能夠考慮卸載流量.
    好比每十個請求,隨機拋棄九個,只放行一個請求到後續處理環節.
    把秒殺的排序模式,變爲隨機抽獎的模式.
    
2.Web服務器集羣層,縮小鎖範圍.
    每次秒殺活動開始以前.先計算活動推出的商品數量,而後分配一個限額到每一個Web服務器.
    好比一個活動推出秒殺商品
    電視,手機,衣服各1000件,那麼每臺服務器的限額就是125件.
    將這個限額寫入ZooKeeper,Web服務器監聽到限額的變化,就會從新初始化各自的商品剩餘數量.

模擬示例:
sql

  1.     private static ConcurrentHashMap<String,Integer> map=new ConcurrentHashMap<String, Integer>();
  2.     
  3.     private void zooKeeperHandle(){
  4.         //將ZooKeeper的變化,初始化到Web服務器全局容器
  5.         map.put("電視機", 125);
  6.         map.put("手機", 125);
  7.         map.put("衣服", 125);
  8.     }

    假設用戶請求秒殺電視機,它只是鎖了該Web服務器電視機的數量。(該Web服務器手機和衣服還能夠繼續併發處理,固然其餘的Web服務器也在同時處理電視機的秒殺請求)
    這樣縮小了鎖定的範圍,增長了系統處理的吞吐量.

    若是這個剩餘數量大於零,則將用戶ID放入電視機購買隊列,而後告知用戶秒殺成功
    若是這個剩餘數量等於零,則告知用戶秒殺失敗.即使別的Web服務器還有電視機的剩餘配額.

3.ZooKeeper層,ZooKeeper變動庫存信息
    假設活動期間,須要修改庫存信息。
    兩種可能,
    第一種,該商品已經賣了500件,電商不想繼續賣了.
    第二種,從倉庫中又找到了一些積壓庫存..

    兩種狀況,都直接修改ZooKeeper中相應商品的配額.
    Web服務器會監聽變化,並從新初始化全局容器.

4.消息隊列層,多消費者處理
    消費者主要是從隊列獲取購買請求,發送至數據庫
    扣減數據庫庫存
    寫訂單明細記錄


5.數據庫層,使用存儲過程代替JDBC調用
    因爲使用了多消費者處理同一隊列,增長吞吐量,避免隊列堆積過大.
    可是多消費者,必然致使數據庫出現單行更新問題.

    單行更新問題就是多個線程,併發修改同一條記錄,致使事務相互阻塞.浪費了數據庫寶貴的處理能力.
    考查下圖.
    假設消費者到數據庫的網絡是1毫秒
    那麼相對於存儲過程,使用JDBC的方式,每一個事務將至少多持有行鎖2毫秒.

    因此進一步優化,能夠考慮用存儲過程代替JDBC


6.數據庫層,庫存單行更新,增長多個槽位.
    單行更新場景

    增長槽位的表結構

   
    使用槽位分散行鎖
    每種商品的庫存,由4個槽位組成.
    事務開始,首先找到剩餘數量最多的那個商品槽位.
    而後扣減該槽位的庫存.
    這樣一個行鎖,能夠變爲4個行鎖,系統吞吐量增長了4倍.
    (其實若是update的影響行數爲0,表示該槽位已經沒有庫存.能夠重複執行這個過程,再另選一個槽位)
數據庫

  1. //開始事務
  2. select 商品,剩餘數量,@槽位:=Slot from 庫存表
  3. where
  4. 商品='電視機' and 剩餘數量>0 and
  5. 剩餘數量=(select max(剩餘數量) from 庫存表 where 商品='電視機')
  6. limit 1;

  7. update 庫存表 set 剩餘數量=剩餘數量-1 where 剩餘數量>0 and 商品='電視機' and Slot=@槽位;
 //若是update的影響行數不爲0,寫訂單明細表
commit;


7.數據庫層,冷熱商品分開.
    某些熱點商品,能夠單獨放置在一個數據庫處理
    好比蘋果手機新品,特賣打折 10w部
    這種註定會熱的商品,應該使用單獨的數據庫處理
    避免和普通商品競爭,堵塞本次活動其餘商品的處理.

參考:
http://blog.itpub.net/29254281/viewspace-1783043/
    
http://jiagou.baijia.baidu.com/article/108134?qq-pf-to=pcqq.group

http://www.infoq.com/cn/presentations/seckill-solution-based-sql
服務器

相關文章
相關標籤/搜索