【系統架構】如何解決熱點資料更新問題

楊奇龍發表於2014-09-15
一 背景
     某個業務線 商品開放開使用者申請免費試用,當某個商品特別吸引人時,比如iPhone6 。肯定有一大波人為了少賣一個腎 瘋狂去搶申請資格。有甚者利用機器人申請註冊,於是簡單的申請操作變成了秒殺行為. 大量請求同時更新資料庫中的同一個商品的申請次數,update 操作給表加上行鎖,導致後面的請求全部排隊等待前面一個update完成,釋放行鎖後才能處理下一個請求。大量後來請求等待,佔用了資料庫的連線。一旦資料庫連線數被佔滿,就會導致後來的全部請求因拿不到連線而超時,業務請求出現無法及時處理的情況,資料庫系統的RT會異常飆高,業務層由於等待出現超時,app 層的連線耗盡,一系列的雪崩效應!

二 解決方案
     從上面的背景分析,解決熱點資料併發更新需要注意核心問題: 減少直接對db層資料熱點的併發更新,本文從業務和資料庫的設計層面來規劃.同時也希望大家提更好的解決思路。
1 前端層面
   前端是整個流量的入口, 正常業務訪問時系統表現平穩,但是當有人惡意請求時,需要加上流控措施,比如常見的
   a 需要使用者回答問題,填寫驗證碼,移動影像等等,防止或者減少有機器人來惡意請求。
   b 頁面上採用防止機器人的判斷 兩秒以內的成功請求一律拒絕。
   c 透過設定nginx ,對同一個ip源的請求次數做限制,防止機器人來申請。
  優點 有效減少或者防止有人利用機器人惡意請求
  缺點 存在一定的誤殺率,錯殺了正常的請求。

2 應用層
    應用程式接收前端前端請求,進行一系列的資料庫操作,在我們規避了惡意請求之後如果還是有大量的資料庫寫訪問請求,我們需要
    a 對業務做降級
限制介面的呼叫次數,降低對資料庫的請求壓力。
選擇不更新請求次數,弱化該商品申請次數的展現。類似於閱讀次數,申請次數 ,與金額,庫存無關的功能點。

    b 透過非同步更新來避免直接寫資料庫 。
      應用使用分散式快取(比如tair)來儲存某項商品的申請次數或者某人的申請次數,以商品id/user_id 或者將where 條件作為key,申請試用人數為value/符合某項具體條件的 count結果為value, 有使用者申請成功則更新申請試用人數。不需要查詢和實時寫資料庫,每隔一定時間/次數將結果寫入資料庫。
      優點:該方法完全依賴於快取,讀寫速度快,不需要實時更新資料庫,減輕資料庫併發寫的壓力;
      缺點:快取不是100%穩定,很容易丟,即使採用持久化的快取,在高併發下有時也可能會出現異常,穿透快取到db ,導致前端業務展現問題。
    
3 資料庫層
    a 將熱點資料拆分,分在不同的庫不同的表中,分散熱點資料,減輕資料庫併發更新熱點帶來的RT升高和應用連線等待時能保證業務能夠正常訪問其他商品表,損失區域性可用性。
    優點:實時讀寫資料庫,前端展示資料的準確性。
    缺點:業務邏輯稍顯複雜。
  b 限流補丁
     針對某些特定的sql語句 從MySQL 層面加以限制,當系統thread_running達到一定值或者某個sql執行時間超過一定閾值則拒絕該sql的執行。(阿里內部已經實現限流版本)
   c 使用MySQL的 thread pool 功能。在併發較大時,one to one的模式會引起Innodb的mutex鎖爭用。當前解決方法是透過innodb_thread_concurrency引數,但是該引數自身也存在鎖爭用,同樣影響了MySQL的效能。
優點:thread pool主要從四個方面考慮:減少SQL併發,使得有足夠的資源:使用執行緒組,獨立管理:使用優先順序佇列,限制併發執行的事務:避免死鎖。
缺點:在測試過程中發現,會有大量的連線等待kernel mutex鎖,但是持續的壓力會導致MySQL的thread running飆高,最終導致MySQL不可用。
三 小結
     在電商類業務中資料庫的熱點/單點更新 一直是DBA和業務方比較關心的問題,它最直觀的影響使用者體驗,比如商品的超賣,系統的可用性。需要不斷的細化解決思路和具體實現比如 熱點商品的屬性是否實時更新 ,庫存數量需要實時展示,訪問次數,請求次數可以非同步延遲展示。本文只是簡單闡述了 對熱點更新的解決思路,還有不完善的地方,歡迎給位提供更好的建議。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22664653/viewspace-1269948/,如需轉載,請註明出處,否則將追究法律責任。

相關文章