熱點帳戶問題和經常使用解決方案【中】

話接上回,上篇闡述了什麼是熱點帳戶,基本財務帳戶如何設計,冪等健和鏈式設計!本篇將針對熱點帳戶在實踐中引起的問題,梳理和拆解業務流,分析問題點,提出七種經常使用解決方案。

1、性能問題初現

上線初期數據量較小,運行正常!
一次大促後,帳戶流水的總數目接近億級別,初現性能問題:系統總體的qps也就10+,但熱點帳戶寫入失敗率偏高,而且隨數據量增長失敗率逐步升高;整個帳戶系統全靠上游有redo標識位不斷重試,才能保證最終寫入成功!web

哈哈,做爲一名擁有三年工做經驗的老碼農,面對問題,要作的第一件事,就是,抽根菸靜靜,準備開搞!算法

2、數據流拆解

拿到問題,抽根菸靜一下以後,分析問題須要三步:梳理數據流,拆解過程,定位問題點。先對財務帳戶更新的數據流進行拆解sql

finance_hot_account_3

鏈式鎖後的基本帳戶操做過程,分爲以下五階段數據庫

  • 請求階段:帳戶操做請求。
  • 查詢階段:查詢當前帳戶信息。主要獲取當前鏈,資金數據等!
  • 計算階段:對鏈和操做的資金進行計算,斷定資金操做合規,同時保證冪等和併發!
  • 寫入階段:事務同步寫入流水和餘額
  • 響應階段:告知上游回調

3、鏈路分析

梳理數據流後,接下來分析每一個階段可能引起的問題。按照優先級,先分析業務問題區域(讀取階段,計算階段,寫入階段),一般問題會出如今業務階段;以後,再分析框架問題區域(請求階段和回調階段),該區域出現問題的狀況偏小,可是一旦出現問題,就是比較有意思^_^!架構

3.1 業務問題區域分析

讀取階段,計算階段,寫入階段三個階段是具體的業務操做,從併發和耗時兩個角度來分析下可能的問題點併發

3.2.1 耗時分析

耗時分爲三塊框架

  • 查詢耗時:RDS擁有億級別數據量,查詢未中primary,但命中索引,業務數據體並未徹底在索引中,所以訪問數據走index match;數據主鍵聚簇,惟一健索引查詢獲取數據,page極難命中cache,也不會命中磁盤電梯算法優化!結合實際狀況,查詢耗時在10-100ms級別
  • 寫入耗時:insert 包含了自增,理論上在數據落盤是追加寫,即便uniq_key去建立索引的狀況下,耗時在ms級
  • 過程耗時:長鏈接狀況下,init conn時間基本能夠忽略,可是讀寫兩次往返數據庫的鏈路時間仍是須要考慮,總體預估在1-2ms之間

從總體上看,預估該階段的耗時在10-100+ms,從實際失敗率來看也基本一致!異步

3.2.2 併發分析

  • 天級QPS:當時分析天級幾十萬單,天級QPS不到10,不高!
  • 瞬間QPS:每一個訂單拆解到資金流後,會同時操做屢次熱點帳戶,瞬間qps相對很高,理論qps就可能達到語言上限,因爲上游鏈路限流1024,按照10級別操做拆分,理論上滿池QPS在萬級別。考慮實際單量,瞬間QPS=單量(10)*拆解量(10),實際的滿額預估QPS可能到100+ !

按照上面分析,在瞬時QPS達到10+的狀況下,熱點帳戶總體延時在10-100+ms,因爲DB在寫入uniq_key保證鏈點惟一,因此出現併發寫入失敗也在情理之中;而且隨着數據量的提高,讀取延時增長,寫入失敗率會繼續增長。高併發

3.2 框架問題區域

請求階段作爲入口,通常也分爲三個小階段性能

  • webserver接收請求
  • 框架加載和路由
  • 基礎校驗

請求階段核心耗時通常存在於框架加載和路由,高併發場景webserver和upstream之間的調用也是一個可能出問題點!當時財務系統,採用歡總封裝的go-thrift,而且其餘模塊並未出現請求階段問題,因此並未對這個階段的latency和併發作一個衡量,重點分析了業務模塊!

4、解決方案

4.1 讀取和寫入階段優化

經過上面分析,目前問題的痛點是併發讀取熱點帳戶數據高延時引起寫入失敗,提高讀性能成爲了關鍵

讀性能提高有兩個基本思路: 讀的時效快和讀的次數少

針對上面兩個基本思路,結合財務帳戶狀況提出了五種提高讀性能的解決方案

  • 【讀快】持久化last record:不從全量數據裏面讀,抽離子帳戶的最新信息,持久化到單獨的表中或者內存中,下降總體數據量,提高了讀性能。缺點是要保證持久化信息的準確性,引入事務寫。
  • 【讀快】縱向切分-時間分庫分表:按照時間進行縱向切分,下降查詢範圍內的數據量,提高讀性能。缺點是跨時間讀不友好,開發量也不小
  • 【讀快】縱向切分-歸檔:歷史數據歸檔是實現相對簡單,跨時間讀也比較友好,隨着數據量的提高,也是必需要作,以後會詳細介紹歸檔方案和選型。
  • 【讀快】橫向切分-業務分庫分表:按照帳戶類型或者城市分庫分表,能夠優化讀寫數據量,同時,跨表讀負擔也會較小。但對於熱點帳戶或者熱點城市,依然聚簇,效果不是很明顯。同時,再次對熱點帳戶進行橫向分庫分表也是極度不推薦,引入的極高的讀寫成本均。
  • 【讀少】階段快照:必定量或者必定時間內的數據,持久化一次。優點是極大的下降讀寫次數;缺點是須要複雜的邏輯來保證undo操做和數據一致性!

五種解決方案各有千秋,做爲一個初期的財務系統推薦採用持久化last record和數據歸檔來保證寫入讀性能和總體讀的數據量。若是系統發展到了中期,推薦按照時間分庫分表。若是發展到了雙11或者春晚某些極端場景,犧牲掉部分準確性,採用階段快照也是能夠的。

4.2 計算階段優化

存在計算階段形成的最大影響也就是引發了兩次數據傳輸,一般是不可避免的,可是若是真的是要進行提高有一種方案通用方案

  • DB計算經過存儲計算,轉嫁計算成本給DB,減小一次鏈路請求。但不太推薦,複雜的sql每每有坑,insert computer from select 還會形成大面積的數據隔離,很容易引發死鎖。

4.3 請求和回調階段優化

請求階段通常有三種形式:同步調用,異步調用和僞同步調用
前兩種調用很是常見:同步爆池的狀況,通常採用限流來降壓,採用漏桶,令牌桶等等策略;異步調用一般採用消息隊列來削峯填谷;這裏重點闡述對於支付和財務系統在請求階段常常採用的僞同步的方式

僞同步流量較早出如今innodb,leveldb等存儲引擎爲了利用追加寫提高寫入性能,採用類WAL日誌來持久化數據。一般僞同步方案採用三件套:WAL日誌+校驗位+廣播消息來完成一次完整的請求!流程圖通常以下

finance_hot_account_4

  • 請求階段:同步請求調用,核心要素追加寫入wal日誌,變動校驗位,完成同步調用!此處追加寫保證了快速寫入,校驗位來保證數據的最終寫入成功。圖中1,2
  • 異步階段:經過讀取wal日誌的核心數據,進行復瑣事務處理,若是成功進入下一階段;若是失敗,沒問題,經過外部trigger來觸發redo操做!若是屢次redo依然失敗,那麼經過undo來回滾數據
  • 回調階段:若是成功,更改校驗位,同時發佈成功廣播消息,關注結果和時效性的模塊,能夠獲取最終成功的標識!若是undo回滾數據,則發佈失敗廣播消息,告知結果失敗!

在僞同步的模式下指標衡量:

  • QPS:僞同步模式,採用WAL核心要素追加寫,因此寫性能能夠極大提高,進而滿額QPS相對直接同步調用也大量提高
  • 時效性:僞同步並不是徹底同步,因此結果須要監聽回調。對於結果強一致的請求,必須監聽回調,確保一致,時效性下降;對於弱一致能夠認爲同步回調即成功,時效性提高。
  • 失敗率:操做知識核心要素追加寫入,真正的操做經過異步保證,總體成功率提高!

對於資金處理過程,大量採用僞同步的請求方式來保證快速寫入和最終一致性

4.4 解決方案總結

總的來講,歸結了七種優化方式(哈哈,上篇寫的八種優化,當時總結的,如今愣是想不到還有啥了^_^)。其中請求和回調的僞同步方式,是在架構層面優化,這個在多數的財務系統和財務系統的內部數據流中都會用到;而讀寫和計算階段的優化,能夠跟進實際業務狀況進行選型處理。

5、事故覆盤

面對各類優化方案,須要結合實際狀況作出取捨,有的是長期方案,有的是快速方案,但必定須要想清楚了再開搞,過程當中有一個對小拽以後影響很大的事故,引覺得戒。

翻車過程:當時覺的讀->計算->寫這個過程,兩次讀DB操做,下沉計算過程到DB後,經過DB計算,能夠減小一次數據庫請求。因而合併了一個大SQL,也就是所謂的insert ( field computer from select),覺的寫了個狂賺酷炫吊炸天的SQL,一上線,庫鎖死了!幸虧有前置的redo flag,全量redo數據恢復,要否則估計直接祭天了!

對於這個複雜大SQL事故,小拽總結了三個方面

莫炫技:沒啥好說的,解決問題的成就感要遠大於炫技!
簡單設計:簡單的設計一般意味着可依賴;複雜的設計要儘量的拆解,想清楚,隊友都理解不了的設計,那就別上線了,可能真的須要再思考拆解下
尊重線上:核心服務基本上線流程必定要遵照,測試,監控和回滾方案缺一不可

6、小結

本篇主要針對熱點帳戶問題提出了七種經常使用的解決方案,下篇將繼續引伸探索下,各類解決方案在不規則高併發場景,例如雙十一,微博熱點事件中如何套用

預知後事如何,下回再聊!

【轉載請註明:熱點帳戶問題和經常使用解決方案【中】 | 靠譜崔小拽

相關文章
相關標籤/搜索