天天看點

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

一 背景

     某個業務線 商品開放開使用者申請免費試用,當某個商品特别吸引人時,比如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和業務方比較關心的問題,它最直覺的影響使用者體驗,比如商品的超賣,系統的可用性。需要不斷的細化解決思路和具體實作比如 熱點商品的屬性是否實時更新 ,庫存數量需要實時展示,通路次數,請求次數可以異步延遲展示。本文隻是簡單闡述了 對熱點更新的解決思路,還有不完善的地方,歡迎給位提供更好的建議。