支付系統如何進行分散式改造
來自專欄 sofastack17 人贊了文章
傳統支付系統面臨的挑戰
隨著近年來移動支付的興起 ,如條碼支付、聲波支付、NFC 近場支付等,隨之還產生了聚合支付把多種支付方式聚合在一起,方便人們的使用,移動支付已經滲透到我們生活的每一個角落,不帶錢包出門已經沒有任何阻礙。這就給傳統的支付系統提出了新的挑戰,用戶量激增,系統容量和性能跟不上了,傳統的架構往往以 IOE 技術為主,採用 scale up 方式以更強的硬體提升系統性能和容量,擴容成本將是巨大的。支付機構是持牌機構都是受監管的,對系統穩定性有強要求,傳統架構下往往都會用冷備的方式來進行容災,意味著又要投入一倍的成本,由於資料庫主備複製的延時,必須等到數據同步完成才可以切換,容災切換時間長。進行分散式改造已經刻不容緩。
更多關於傳統架構與分散式架構對比請參考《集中式架構與分散式架構比較》
將單點改造拆分成多個點
分散式架構在容量、性能、穩定性、成本方面都具有巨大的優勢。在高可用方面,核心思想之一是「解決一切單點」,單點容易出現故障,性能方面也可能成為瓶頸。
垂直拆分能更清晰化模塊劃分,區分治理;
水平切分能解決大數據量性能瓶頸問題;
分散式改造主要是將這兩者結合起來,對傳統架構進行全面的改造。
分散式改造之垂直拆分
垂直拆分就是將原來一個整體的系統按業務模塊拆分成多個系統,系統內部數據是自包含的,不會與別的系統共用資料庫,系統與系統之間的交互通過暴露和調用服務來實現。那麼如何按照業務來拆分呢?
為了方便理解,首先我們來看一下一筆支付過程是如何進行的:
- 商戶發起收單請求,經過 API 網關,調到產品層的「在線收單」產品
- 調用收銀台選擇支付方式,也可能直接進入支付環節,創建交易流水
- 進行支付處理,通過金融交換從銀行扣客戶帳,記錄帳務流水,入商戶帳,記錄賬務流水
- 對交易按照費率進行收費,記錄收費的帳務流水。此時會非同步觸發營銷和風控策略
- 日終會非同步進行會計記帳(也有同步記會計帳的)、業會核對、清結算和對帳處理
從這個過程可以大概推演出支付系統的一般應用架構:
圖:支付系統的應用架構
應用架構定義一個大型軟體系統由哪些應用子系統構成,以及應用之間是如何分工和協作的。好的應用架構抽象合理、協作有序、易於擴展、能夠復用。有了這個應用架構,我們就可以非常清晰的根據應用架構劃分的子系統來進行垂直拆分。
從架構上來說,分為四層:
圖:支付系統的分層
渠道層:商戶和客戶的交易請求的入口。一般會劃分以下系統:商戶網站、用戶網站、無線接入、API 網關。
產品層:通過基礎服務層提供的服務組裝成具體業務場景功能,對客戶、商戶運營等人員提供服務。一般會把服務商戶的功能劃分為商戶域,服務 C 端用戶的劃分為用戶域。可以按照這兩個域拆分成兩個子系統,也可以更進一步根據不同產品特性再拆分,比如商戶域中的收單產品、虛擬產品、垂直行業產品。
公共服務層:將各個產品都需要使用的些服務抽像成公共服務。一般會劃分:收銀台、交易支付、計費等系統。比如說產品層可以通過組裝各種交易類型和收費規則形成不同的產品。
基礎業務層:支付系統的核心,資金和客戶信息的處理都在這裡。一般會劃分三大子系統:帳務核心、會計核心、會員核心。
其它支撐系統:
網關:負責與銀行、銀聯等金融機構進行資金交換,與外部合作夥伴接入,如渠道拓展商、行業客戶等。一般劃分:銀行接入網關和合作夥伴接入網關。
運營支撐:貫穿於四個層的是運營支撐域:一般會劃分運營支撐、安全、風控、營銷子系統。
垂直拆分本質上是服務化改造,除了上面講的按業務拆分,還需要一套分散式服務框架的支撐。
分散式改造之水平拆分
前面講的垂直拆分只是把系統按業務模塊劃分到不同的子系統,資料庫也分到了不同系統,但沒有解決單表大數據量的問題,而水平切分就是要把一個表按照某種規則把數據劃分到不同表或資料庫里。簡單的說就是做分庫分表。
在做分庫分表之前我們需對數據模型進行分類,分為「流水型數據」、「狀態型數據」和「配置型數據」。
- 流水型數據:像流水一樣不斷增長的數據,各條數據間是獨立的。如支付訂單、交易流水、帳務流水(入帳/出帳)、會計流水等。
- 狀態型數據:代表一個對象當前的狀態的數據。如會員信息、客戶信息、帳戶信息、會計帳。
為什麼有會員信息還有客戶信息?會員往往是註冊在支付平台的用戶,一個人可以註冊多個會員,但是一個自然人只可能有一個客戶信息,一個會員通過實名認證後就關聯上了客戶信息。無論一個客戶註冊多少個會員,實名認證後都只有一個客戶信息。
- 配置型數據:系統中用作為配置的數據。如產品、手續費率、分支機構信息、支付路由規則、會計科目等。
流水型數據會不斷產生,且各條數據間是獨立的,天然適合進行分庫分表。
狀態型數據讀寫比相當,每一次寫操作必須基於前一個正確的狀態,可以評估一下數據量的大小,數據量如果大或者要實現單元化架構,也需要進行分庫分表,提高並發處理能力,同時方便隔離故障影響。
配置型數據,讀多寫少,強依賴讀,弱依賴寫,不要求嚴格的讀一致性,且配置型數據一般數據量不會很大,不需要進行分庫分表設計。但是業務處理中往往又需要用到,傳統架構的老系統可能使用了一些關聯表操作,關聯到了配置數據,分庫後其它數據與配置不在一個庫,不能進行關聯表操作,由於配置型數據不要求嚴格的讀一致性的特點,可以將配置型數據載入到分散式緩存里,由業務代碼來做「join」。
那麼分庫分表按照什麼規則來拆分呢?通常不會按實體 id 進行 hash 取模的方式來拆分。因為希望同一個用戶的數據能夠在同一個資料庫中,盡量避免產生分散式事務。業界普遍的做法是通過用戶維度來進行拆分。由於不同實體 id 的值不同,且不能保證每個實體和請求中都包含用戶 id,所以簡單的用實體 id 或用戶 id 進行 hash 取模將不能保證同一個用戶的數據都落在同一個分片。
一種推薦做法是,在用戶創建的時候給該用戶隨機或一定規則(如地區)生成一個兩位的分片號 00~99(兩位意味著可以分成百庫百表,通常夠用了),那麼在生成與該用戶相關的所有實體的 id 的時候,都約定把這個分片號拼接到這個 id 中。在分散式數據訪問框架中進行路由選擇時,就可以取 id 中的分片號進行路由,而不依賴於用戶 id。且在排查問題的時候也非常方便定位數據的存儲位置。
下面是一個參考的 id 生成規則示例:
所以數據水平拆分除了需要一個強大的分庫分表數據訪問中間件,還需要一個分散式序列生成器。當然這個生成器也可以是集成在分庫分表數據訪問中間件中的一個功能。
那麼如果一筆交易涉及多個用戶按誰的 id 來拆分呢?比如一筆轉賬或支付,涉及轉出方/轉入方或支付方/收款商戶。這種情況一般可以按資金轉出方來拆分。
分散式改造後帶來的問題如何應對
- 分散式事務產生
由於按用戶維度進行了分庫分表,可能存在跨資料庫的事務,比如說,轉賬交易中轉出方和轉入方的賬戶不在同一個資料庫中,這就產生了分散式事務。通常不會用 XA 協議來解決,因為 XA 協議鎖資源性能太差,通常是通過 TCC 柔性事務來解決。具體可以參見進階閱讀《分散式事務綜述》。
2. 跨表查詢如何解決
由於分庫分表後,不能進行跨庫的連表查詢,原來的一些很常見的查詢操作變得很麻煩。對於不是以用戶為維度的匯總查詢也非常麻煩。比如說支付交易流水是按發起方用戶(支付方)進行拆分的,用戶需要查詢自己的賬單很容易。但是商戶要查詢賬單就比較麻煩了,要去所有的庫里遍歷、匯總、分頁。也非常耗系統資源。所以一般會做一些數據冗餘,例如專門實現一個賬單系統,通過消息隊列非同步將用戶的交易流水同步過來,T+1 跑批再按商戶維度進行拆分,並生成商戶賬單。查詢帳單都從帳單系統中查詢。
還可以通過異構索引來查詢和做 OLAP 分析,異構索引就是將數據同步到 ElasticSearch,利用 ES 的強大索引能力來做查詢和分析,為了使業務更容易使用,可以利用數據訪問代理層來屏蔽底層是路由到資料庫還是路由到 ES。
3. 如何進行數據同步
企業都有做大數據分析的需求,需要將數據同步大數據平台,如 Hadoop。分庫分表之後,數據同步會比較複雜,畢竟之前是單表同步到 Hadoop 比較簡單,但是 100 張表同步到 Hadoop 里會複雜一些。這時就需要設計一套專門的數據模型管理平台,數據模型、分庫分表規則等由這個平台來管理,當需要使用數據的時候通過(應用/邏輯表)維度訂閱數據即可,不用單獨訂閱物理表。不僅是數據同步,凡是有業務需要用到各種數據,都可以通過這個平台來訂閱,幫助企業數據業務快速發展。
4. 分庫分表後批處理任務怎麼處理
批處理任務,比如有日終對賬、清算、生成賬單等,原來在一個資料庫中的時候,由一個應用 Server 去資料庫中撈取流水就可以了。但是分庫分表後流水都落在很多庫里,一個 Server 去每個庫里遍歷顯然不是一個很好的辦法,且不能充分利用機器資源,提高批處理效率,甚至由於處理的數據量太大在日終低峰期內根本無法完成任務。
前面提到各條流水數據之間沒有關聯的,完全可以並發的進行處理,每個 Server 撈取一個分片的數據進行處理。那麼就需要有一個很好的調度系統來協調,可以採用三層調度的方式。
圖:三層調度示意圖
- 第一層 split:把任務按照分片規則拆分成多個 Load 任務,並發送到集群中的 Server 去執行。
- 第二層 load:每個 load 任務撈取一個分片的數據,逐條創建 execute 任務,並發送到集群中的 Server 去執行。注意:撈取數據要進行流量控制以免數據量太大把集群打滿。
- 第三層 execute:執行具體的一條數據的邏輯。
三層架構並不是說一定都需要三層,可以根據業務邏輯來定製只有兩層也可以。
5. 如何進行數據擴容
通常可以採用「預分配」的方式來做,即一開始就按一個比較長期的容量來規劃分片數,比如百庫百表。但實際上一開始並沒有這麼大的量,所以實際只有兩個資料庫 Server,在這兩個 Server 上分別建 50 個 schema,邏輯上仍然是 100 個分庫,物理上只有 2 個資料庫 Server。當容量不夠的時候,為了保證數據的均衡,通常會採用成倍擴容的方式,再加兩台資料庫 Server,然後分別遷移 25 個 schema 到這兩個資料庫 Server 上,數據也搬過來。由於數據同步有延時,全量數據同步完成後,兩邊的 schema 都禁寫,待增量數據同步完成後打開新的 schema 寫,會產生短暫的部分用戶交易失敗,重試一下即可,在低峰期做遷移,產生小範圍失敗一般是可以接受的。由於邏輯分片數沒有變化,擴容成本比較低。通常不會用改變分片規則的方式來擴容,因為改變分片規則需要進行數據重新分布,成本和風險巨大。
6. 如何進行容災
- 同城容災:通常可以同城多機房部署應用,資料庫只有一個機房處於 Active 狀態,所有機房的應用都連這個機房的資料庫,另一個機房的資料庫為備庫,進行主備複製,當備機房發生災難時業務不會中斷,但業務會跌一半,當主機房發生災難時,資料庫切換備庫,會有短暫的業務中斷。
- 異地冷備:應用也是異地多機房部署,由於異地網路延時不可忽略,異地備機房是處於 standby 狀態,正常是沒有流量的,冷備機房採用資料庫主備同步的方式同步數據,這種方式災備切換時間長,成本投入高。
- 異地多活:應用採用異地多機房單元化部署架構,每個機房的應用都是可以提供服務的,單元內是自包含部署全量應用,每個單元服務多個分片的用戶,單元化架構可以參考《素描單元化》。由於異地網路延時是不可忽略的,數據層的容災方案也是分「流水型」、「狀態型」、「配置型」數據採用不同的容災策略。具體可參考《分散式系統數據層設計模式》。
7. 如何更好的排查和分析問題
分散式改造後整個系統架構已經是服務化了,原來通常可以通過查本地日誌來定位問題。但現在一個交易由若干個系統協同完成,我們需要一套分散式鏈路跟蹤系統或 APM(應用性能管理)系統來協助我們看清整個系統的全貌,分析排查問題。那麼如何進行分散式鏈路跟蹤呢?可以通過 OpenTracing 標準對整個分散式架構中的中間件和應用進行埋點或自動植入探針實現。
總結
分散式架構有著海量、成本、穩定、速度的優勢,但它也不是銀彈,分散式改造是一個較為複雜的工程,既需要熟悉業務,能夠設計出整個系統的業務架構,按照業務架構來進行垂直拆分,又需要熟悉數據模型,區分「流水型」、「狀態型」、「配置型」數據,根據不同類型數據的特點將它他按用戶維度進行拆分,還需要熟悉分散式中間件的運用。分散式中間件在整個分散式架構中起著至關重要的作用,將技術構架與業務結合起來。螞蟻金服通過多年金融級架構的演進,經過多年雙十一大促的驗證,已經形成了一套業界領先的金融級分散式架構,請參考《金融級分散式交易的技術路徑》。
歡迎大家共同打造 SOFAStack https://github.com/alipay
推薦閱讀:
※分散式鎖
※分散式事務 -- 最佳實踐方案匯總 -- 看這1篇就夠了
※DevOps 漫談:基於OpenCensus構建分散式跟蹤系統
※分散式系統設計:服務(多節點)模式
TAG:分散式系統 |