Categories
程式開發

“給高速行駛的汽車換輪胎”,攜程度假產品系統改造實踐


一、前言

本文將介紹在業務持續發展環境中,複雜系統的改造過程以及實施的一些經驗,希望能給面對同樣問題的同學提供一些借鑒思路。

1.1 背景

度假產品系統,作為攜程度假部門自營業務人員與供應商的上貨平台,為旅遊產品的預訂以及訂單處理流程提供產品信息的數據支持。作為業務系統的底層數據,有如下特點:

1)業務複雜度高

到2020年,系統經歷了8年的業務邏輯積累,支持多種產品形態,包括:跟團遊,自由行,周邊遊,簽證,以及當地嚮導等,30多種產品和資源類型,包括:保險,火車票,一日遊,接送機等等。還與交通和住宿部門對接,支持多種大交通資源的匹配規則,包括機票,酒店,火車票等。

2)系統架構陳舊

改造前,系統架構基於.NET平台開發,使用SQLServser數據庫。隨著時間和業務邏輯的積累,系統中存在大量陳舊的代碼邏輯,數據庫中也有很多廢棄的表和字段,使用SQLServer的複制分發功能實現讀寫分離,不同系統之間依賴數據同步,這些陳舊的架構導致系統擴展性差,數據實時性低。

1.2 問題

業務複雜度高和系統架構的落後,帶來的問題是系統問題多,開發效率低。

1)複製分發延遲,影響產品售賣

數據庫800多張表,包括核心表和非核心表,劃分不清晰相互影響。非核心業務往往存在大量的數據更新和讀取,影響數據庫性能,從而影響核心業務。表的結構設計不合理,不同領域的數據結構劃分不清晰混在相同的數據表中,產品主表有200多個字段,其中包括很多廢棄的字段。 SQLServser的複制分發在寫庫大量數據更新時,分發庫存在延遲影響正常業務。

2)開發效率低,問題頻發

自營與供應商使用兩套獨立的產品系統,而預訂和訂單處理流程只使用一套數據,數據通過臃腫的WCF架構同步,存在數據不同步的風險。系統重複開發,浪費研發資源。後台站點直接讀寫DB,多模塊寫入點分散。這些架構的複雜度帶來涉及人員多,排障流程長等問題,效率低下。

1.3 措施

針對系統現狀,通過對系統重構,解決複製分發延遲問題,降低系統整體複雜度,避免重複開發,提高開發效率。

系統的改造是伴隨著業務需求壓力並持續優化改造的過程,重點有兩方面:

1)存儲重構

數據庫從SQLServser遷移到MySQL,包括消除數據庫的複制分發,重新按領域模型拆分錶,將核心數據與非核心數據隔離,通過消息系統替代輪詢數據等。

2)架構升級

減少重複建設,去掉臃腫的數據同步過程,將分離的兩套系統合併。從.NET站點轉變為前後端分離的系統架構。

二、存儲重構

2.1 SQLServer轉到MySQL

數據庫從SQLServer向MYSQL遷移,主要有三個難點:

1)數據源切換,寫入點分散,改造成本高,週期長。

首先是成本高,寫入點分散的問題。產品系統的應用有多個,同一個模塊的數據在不同的功能中都有修改的場景,漏改會引發數據不一致問題。

由於產品系統作為度假整體系統的一部分並且作為基礎數據的支持,改造過程有上下游的依賴,大量的應用直接讀取DB,數據讀取點多。如果逐個表逐個字段遷移再與相關依賴系統協調切換數據源,整個改造週期會非常長。

2)下游切換,依賴多,邏輯複雜。在大量下游系統直接依賴數據的情況下,每個下游系統都有各自的業務邏輯,使用的數據字段和方法各有不同,邏輯分散在各處。

3)數據一致性。基礎數據的數據源切換,一旦發生問題,對業務影響大。

為了解決這些難點,我們把數據的遷移分成三個階段,如下圖所示:

“給高速行駛的汽車換輪胎”,攜程度假產品系統改造實踐 1

數據庫遷移的三個步驟

1)建立數據同步機制,提供API規範數據訪問,下游快速切換到MySQL數據源。

這一步的目的是避免按表分步切換拉長改造週期。首先是需要提供產品數據查詢統一的API給下游系統,API中直接讀取MySQL的數據,在數據切換時保證數據的完整性,在MySQL中建立數據,通過數據同步機制保證的一致性。這樣下游系統不需要逐個字段或逐個表進行遷移,可以基於功能模塊對接API。

2)數據源雙寫,逐步斷開數據同步。

這個步驟是逐步對原來功能模塊寫數據的場景進行改造,在新老DB共存上下游逐步切換的場景中,保證業務的穩定性。在寫原SQLServser數據的同時,將數據再對MySQL數據庫寫一份,以便於下一個階段徹底解除對SQLServser數據的依賴。

數據表有三類:

第一類是業務需求功能改動相關表,這類表隨著業務需求中涉及到的改動點,把依賴的數據表整理出來重新開發功能。

第二類是沒有新需求功能的相關表,這類表需要研發人員主動梳理並作為技術改造的日常任務進行切換,經常伴隨著業務需求的壓力以及需要協調各方資源推進完成改造。

第三類是其他系統表,這類表由於歷史原因保存到產品系統中,需要梳理出來並且由各系統安排遷移。

這個階段有兩個經驗:

第一是通過監控把控風險。由於系統的歷史債,寫入點分散,開發人員也並不能完全知道所有的寫入點,如果漏掉某一個寫入點對業務的影響都非常大。解決這個問題是建立數據的監控機制,數據雙寫的同時對源數據進行監控,如果發現SQLServer與MySQL相同主鍵數據不一致時進行報警。

第二是數據補償機制,我們開發了數據補償工具,當發生數據不一致時,以SQLServer數據為準向MySQL進行數據補償。監控到數據不一致問題,立即補償數據,恢復同步,再把漏改的點梳理清楚。

3)斷開雙寫,完成切換

在雙寫過程中,下游系統消除了對SQLServer數據庫的直接訪問,產品系統作為數據的源頭,通過DBTrace工具排查對原有表的讀寫是否都已經解除,在都解除的情況下斷開SQLServser的數據寫入,完成切換。

存儲重構的過程以轉MySQL作為改造的主線推進,改造過程中也進行了其他多方面的改造,後面逐個介紹。

2.2 消除複製分發

改進前通過SQLServser的複制分發技術實現讀寫分離,SQLServser的兩個源頭數據庫通過複製分發中間庫,分發到14個目標庫中,800多個源頭表,3800多條複製分發鏈路,部門內外247個應用直接訪問數據庫。

“給高速行駛的汽車換輪胎”,攜程度假產品系統改造實踐 2

複製分發改造前

初期,梳理出所有依賴的系統以及相關SQL語句,涉及到幾乎所有度假子系統,針對各個系統對於原始數據的需求與轉MySQL的過程同步進行,遷移數據並提供替代直接訪問DB的API,下游系統接入API切換數據源。改造後的依賴如下圖。

“給高速行駛的汽車換輪胎”,攜程度假產品系統改造實踐 3

複製分發改造後

去除複製分發的過程對轉MySQL整體進度提供了很大的推進作用,以依賴應用數和分發鏈路數作為跟進目標,每週跟進解耦進度,對於分發表中沒有讀取的表逐步下線。分發表下線的進度同時也是轉MySQL過程中下游依賴解耦的進度。這個改造持續了一年半的時間改造完成。

改進後的數據鏈路,DB內聚成應用的存儲層,不再允許跨業務的直接訪問DB,保障了數據安全性,提供全新的API從MySQL獲取數據以及Redis緩存機制提高了性能和可用性。

2.3 產品&資源結構拆分

產品與資源是不同的數據模型,而在數據庫的早期設計中是混合在一起的,存儲在相同的一套表中,帶來的問題是:

1)冗餘字段多,邏輯交錯重疊,下游數據使用方在使用數據的時候需要處理不同的字段和多個邏輯分支,邏輯複雜,容易出錯。

2)由於主表的字段多,一次查詢220個字段,存在數據庫的IO問題。

在轉MySQL的過程中,將兩類模型拆分,各自保存獨立的字段。兩套表根據數據量做了不同的分錶策略,API也分別提供不同的接口給下游系統。

“給高速行駛的汽車換輪胎”,攜程度假產品系統改造實踐 4

產品和資源數據結構拆分

2.4 核心&非核心隔離

改造前核心數據與非核心數據混在相同的DB中相互影響,而兩類數據的特點不同,根據讀寫量,是否影響預訂流程,是否需要部署DR等方面區分核心數據與非核心數據進行隔離。

核心數據庫:

  • 讀多寫少
  • 影響主流程
  • 讀寫分離
  • 部署DR

非核心數據庫:

  • QPS小,歷史數據量大
  • 不影響主流程
  • 一主一從
  • 無DR

2.5 消息替代輪詢

SQLServser作為數據存儲時,下游系統很多業務場景需要監聽數據的變化,舊的做法大部分都是輪詢關注的表,通過時間戳獲取增量數據同步到本業務的數據庫或者進行業務處理,由於之前介紹的數據庫本身的複制分發延遲問題以及受輪詢週期的影響等因素,這個方案在數據實時性上表現較差。

解決這個問題的方案是在系統改造過程中建立消息機制。下游系統通過消費消息完成業務邏輯,這樣的方式降低了系統間的耦合度,也提高了數據實時性。

“給高速行駛的汽車換輪胎”,攜程度假產品系統改造實踐 5

產品消息系統

消息分為兩類:

第一類是Canal消息,在數據發生變化時發送變更內容。

“給高速行駛的汽車換輪胎”,攜程度假產品系統改造實踐 6

原理相對比較簡單:

1)canal模擬mysql slave的交互協議,偽裝自己為mysql slave,向mysql master發送dump協議;

2)mysql master收到dump請求,開始推送binary log給slave(也就是canal);

3)canal解析binary log對象(原始為byte流),Canal的原理來源自簡書,有興趣的同學可以了解一下,https://www.jianshu.com/p/87944efe1005

第二類是系統消息。這類消息是在產品信息發生變化的時候,由功能觸發推送對應內容的消息。

這兩類消息結合,Canal消息的使用已經滿足了大部分的功能場景,但是缺點是消息的數量是基於數據變化的,產品信息的一次變化可能更新幾十張表,成百上千條數據。而係統消息也不是每個功能都需要發送,在特定關鍵信息發生變化的時候發送對應的信息給下游系統消費來彌補Canal消息量太大產生的問題。

三、架構升級

除了存儲重構系統,最大的問題還是歷史架構的技術債,自營產品系統面對攜程業務人員基於內網使用,功能和權限更大,而代理業務通過外網訪問,功能更少而交互更加方便快捷,通過WCF同步數據,在提供給後續預訂以及訂單處理流程時底層數據結構相同。

架構臃腫複雜,數據實時性差,問題頻發,功能重複開發。以上的弊端需要對系統重新規劃,在系統重構中有兩個思路:

1)將分離的系統合併,避免重複開發,減少數據同步帶來的實時性問題。

2)新的架構前後端分離,前端交互分別設計,存儲層API結構統一。

“給高速行駛的汽車換輪胎”,攜程度假產品系統改造實踐 7

系統合併前後對比

3.1系統合併

“給高速行駛的汽車換輪胎”,攜程度假產品系統改造實踐 8

系統合併

這兩套系統操作方式和功能上都有差異,系統功能複雜改造過程影響面廣,合併過程是將系統按照獨立可拆解的功能和數據模型分解,拆分為可獨立切換的模塊,每個月發布一個版本,分成六個版本分別切換。

新老系統進行灰度切換,每週逐步開放新系統流量收集問題迭代改進。

功能上“取長補短“,兩個系統在功能設計上有各自的優點,比如自營系統強在功能的靈活性,而代理系統的交互簡單操作效率更高,功能的設計重新審視各功能以及權限劃分對於用戶統一考慮,通過權限隔離不同用戶的可操作範圍。

底層架構“求同存異”,“同“是指系統的底層數據結構和領域模型相同,對下游的API相同。 “異”則是對於前端系統對於不同的交互和展現方式可以不同。

“給高速行駛的汽車換輪胎”,攜程度假產品系統改造實踐 9

模塊劃分

3.2 前後端分離

改進前,系統是ASP.NET後台站點直接訪問數據庫,相同的功能重複開發,系統限制規則不統一,兩類用戶相互投訴。

前端使用ReactJS技術,內外網分別部署,通過權限隔離用戶。後端使用基於Java開發的API。底層存儲包括MySQL關係型數據庫。系統數據變化時通過ElasticSearch建立索引,Hbase保存日誌內容。通過統一的Service與外部API對接。

“給高速行駛的汽車換輪胎”,攜程度假產品系統改造實踐 10

系統邏輯架構

四、結語

業務系統的技術改造是一個持續的過程,需要作為開發人員的日常工作主動推進,開發人員需要具備發現技術債的敏銳性以及改進系統的決心,同時還要能控制好業務需求與技術改造工作的安排。這裡總結三點:

1)控制風險

在系統改進的過程中,對於復雜和影響範圍廣的業務場景,風險的控制十分重要。發布前需要製定好灰度切換方案以及問題的處理預案,按照什麼步驟切換,每次多少流量,出現問題後如何回退,開關按照什麼粒度製定等等。

2)尊重用戶

新系統的上線,重構功能時要充分考慮原系統功能,考慮重新設計的功能是否符合用戶的使用習慣,是否給用戶使用效率帶來正向影響。刪減功能的時候需要充分了解原功能解決的問題以及新的功能是否能滿足,盡量提供“回到舊版”功能。並在舊版中埋點以關注是否舊的系統功能仍有用戶在使用,思考“為什麼”和如何解決。

3)新的問題

重構系統中使用新方案和技術的同時,也會遇到新的問題。比如數據庫的分錶帶來的是范圍查詢變得複雜,之前用一個SQL語句就能完成現在需要按照分錶逐個遍歷,性能上是無法接受的。這就要使用其他的方案來解決問題,比如使用ElasitcSearch將過濾的字段建立索引。

最後介紹一下這些改進後的效果,兩類用戶都獲得了之前無法使用的部分新功能,系統可用性和穩定性得到提升,避免了重複開發,溝通成本更低效率也更高,系統數據實時性更高,以前頻發的數據不同步等問題得到了徹底的解決。

作者介紹

yk,攜程研發總監,致力於系統性能優化和研發效率的提升。

本文轉載自公眾號攜程技術(ID:ctriptech)。

原文鏈接

https://mp.weixin.qq.com/s?__biz=MjM5MDI3MjA5MQ==&mid=2697269453&idx=1&sn=ec980cc7a0a535ba8ece056438f7bcf2&chksm=8376eff9b40166eff09882a70691280129e97010fdee9dbeea877c726761e9d31ade13706634&scene=27#wechat_redirect