從SQL Server到MySQL,攜程核心系統無感遷移實戰

 

 前言

攜程酒店訂單系統的存儲設計從1999年收錄第一單以來,已經完成了從單一SQLServer數據庫到多IDC容災、完成分庫分表等多個階段,在見證了大量業務奇蹟的同時,也開始逐漸暴露出老驥伏櫪的心有餘而力不足之態。基於更高穩定性與高效成本控制而設計的訂單存儲系統,已經是攜程在疫情後恢復業務的必然訴求。

 

目前攜程酒店訂單系統,在着在業務高增長的同時,也面臨着信息讀寫管理能力受制於數據庫自身性能與穩定性的窘境。綜合分析,一則爲攜程服役了十多年的SQLServer服務器集羣的磁盤容量設計,已經跟不上時下新增訂單量的空間訴求;二則在系統能力提升上造成了各大業務系統巨大的底層瓶頸與風險,同時又相比業界主流已基於MySQL架構設計存儲系統而言,我們的訂單存儲系統仍基於SQLServer構建也整體推高了運營成本。

 

爲了支撐未來每日千萬級訂單的業務增長目標,同時滿足高可用、高性能、高可擴展的高效成本控制期望,我們爲酒店部門的訂單DB所有訪問開發並落地了一套穩定且可靠的統一中間件封裝方案,對現狀收斂並提供了全局統一的熱點緩存系統,徹底解決了當下訂單上層應用與數據庫間直連的方案缺陷。

 

新系統由中間件服務統一實現了對上層應用提供數據鏈服務,並達成了爲現有依賴訂單庫的應用以及其他直接或間接的數據應用無感的實現存儲底層由SQLServer向MySQL技術架構遷移的目標。

一、架構綜述

通過對現有系統瓶頸的分析,我們發現核心缺陷集中在訂單數據緩存分散導致數據各端不一致,各訂單應用則與數據庫直連又造成可擴展性差。通過實踐我們編寫中間件抽象並統一了數據訪問層,以及基於數據庫部署架構鏡像構建了訂單緩存統一管理熱點數據,解決了各端差異。

 

 

 

                                  圖1.1  存儲系統架構圖

二、應用場景

 1、新單秒級各端同步

從訂單的提交到各端可見的速度爲存儲服務的核心指標之一,我們對數據鏈的主要環節進行了優化,覆蓋了新單同步、消息實時推送、查詢索引構建以及數據平臺離線歸檔等主要環節,使大系統內數據到達速度在3秒以內,即用戶剛下完單即可跳轉我攜列表可見。

 

  • 當新用戶創單時,同步服務作爲數據鏈入口將用戶訂單數據通過中間件寫入訂單庫,此時中間件同時完成訂單緩存的構建;

     

  • 當訂單完成入庫行爲和熱點數據構建後拋訂單消息,實時輸出給各子系統;

     

  • 當新單入庫完畢即刻構建訂單明細信息的ES索引,爲第三方提供檢索支持;

     

  • 最後數據平臺T+1實施當日數據的歸檔供BI等各類離線業務使用。

 

 

                                                                  圖2.1 數據鏈

 

2、自動發單與工作臺

對客、商、員工工作臺三端的支持是訂單存儲系統的基本角色,圖2.1數據鏈在新單提交後爲自動發單與工作臺起到的銜接作用功不可沒。自動發單即在客人提交訂單後,以最快的響應速度向商戶發送訂單明細信息進行覈實貨位、確認訂單等流程。工作臺則協助員工介入流程及時獲取訂單處理人工事件。

 

 

 

                                    圖2.2 基於存儲系統的發單與工作臺關係(縮略細節)

3、查詢與數據分析

基於訂單數據爲核心的主要分爲在線查詢和數據分析兩條業務線,以對詳情查詢爲例,訪問QPS終年保持在高位,每逢假期高峯則容易造成查詢瓶頸,根因覆盤後在本次架構升級中我們做了調整來優化相關場景的高可用性。

 

  • 在線查詢以訂單緩存爲主,訂單提交即構建熱點緩存紓解查詢壓力,並可按配置時間參數長時段有效。

     

  • 非在線查詢場景,以實時消息推送並結合Hive數倉T+1方式交付,凡需要長週期訂單數據的場合(例如實時報表)均接入訂單消息實時計算。離線BI按年度等大批量數據分析時使用Hive表,並每日凌晨低峯時段以從庫低頻訪問的方式實施數據同步。

 

如此以上,我們將訂單主庫的訪問保護在訂單緩存、實時消息、Hive數倉三駕馬車之後,與業務盡最大可能的解耦。

 

三、系統升級實踐 

在對攜程核心存儲系統進行更新換代的過程中,貫穿全程需要做到的是熱遷移,並達成所有操作對數據鏈路上的各應用透明無損的目標。我們的設計通盤分析了集團數據鏈路的特性,由訂單緩存系統提供數據庫鏡像降低應用與數據庫的直連耦合,繼而再通過中間件對應用透明掉數據源於SQLServer / MySQL的物理關係,提供底層熱遷移的操作空間。

 

結合無損遷移的工藝設計,注重對每一筆數據庫流量的可見及可控,支持全庫、Shard級、表級、CRUD操作級的流量分配策略,提供了底層數據遷移足夠的實施手段。數倉銜接設計則側重於解決數據平臺百億級離線數據與雙庫在線期間的同步問題,以及解決全量接入MySQL期間產生的數據問題。

 

以下將分三個部分分享我們在這一過程中學到的經驗。

1、分佈式訂單緩存

隨着業務發展,用戶數和訪問量越來越大,訂單系統應用和服務器的壓力也與日俱增。在沒有引入訂單緩存之前,每個應用獨立連接數據庫,造成查詢出來的數據無法在應用間共享,並且DB每秒查詢量和連接數都有上限,而酒店核心交易鏈路基於DB存儲,存在單點故障風險。

 

經過埋點數據分析,訂單系統是典型的讀多寫少,爲了共享熱點查詢數據以及降低DB負載,一個有效的辦法就是引入緩存,如圖3.1,用戶的請求過來時,優先查詢緩存,如果存在緩存數據,則直接返回結果;緩存沒有命中,則去查詢DB,根據配置策略校驗DB結果數據,校驗通過則將DB數據寫入緩存留作後續查詢使用,否則不寫入緩存,最後返回DB查詢結果。

                                                                              圖3.1 訂單緩存基本設計

 

關於引入新的緩存組件後的硬件開銷,可通過收斂原來各應用分散的硬件資源來降低總成本,但還會因爲中心化管理帶來可用性挑戰以及數據一致性等問題,故需要充分對現有系統進行容量評估、流量估算和緩存表價值分析。只緩存訪問量高的熱點數據表,通過恰當的緩存結構設計、數據壓縮和緩存淘汰策略,最大程度提高緩存命中率,在緩存容量、硬件成本和可用性之間做好權衡。

 

傳統的緩存設計,是一條數據庫表記錄對應一條緩存數據。而在訂單系統中,一個訂單查詢多表的場景很常見,如果採用傳統設計,在一次用戶查詢中,Redis的訪問次數是隨着表數量增加的,這種設計網絡IO較大並且耗時較長。在盤點表維度流量數據時,我們發現有些表經常一起查詢,不到30%的表其查詢流量超過90%,在業務上完全可以劃分爲同一個抽象領域模型,然後基於hash結構進行存儲,如圖3.2,以訂單號作爲key,領域名稱作爲field,領域數據作爲value。

 

這樣無論是單表還是多表查詢,每個訂單都只需要訪問一次Redis,即減少了key,又減少了多表查詢次數,提升了性能。同時value基於protostuff進行壓縮,還減少了Redis的存儲空間,以及隨之而來的網絡流量開銷。

 

                                                                                          圖3.2 基於domain的存儲結構簡述

2、無損遷移工藝

如何做到無損熱遷移是整個項目最具挑戰性的地方。在工藝設計之前我們的前置工作首先完成了中間件的開發,通過中間件將數據庫與業務層應用一分爲二。其次抽象Dao層實現領域化,並由數據領域層嚮應用提供數據服務,領域之下適配SQLServer和MySQL兩種數據庫並統一封裝。以此爲基礎才能爲以下述工藝設計實施無損熱遷移。

 

  • SQLServer和MySQL雙庫在線,實施雙寫,主寫SQLServer,同步副寫MySQL,如果SQLServer操作失敗則整體失敗,回滾雙寫事務。

     

  • SQLServer和MySQL之間增加一路同步Job,實時查詢SQLServer最近時間窗口變更的數據進行一致性校驗MySQL中的條目,差異點追齊,可以確保雙寫期間不可預期的兩邊不一致,特別是還殘有直連寫SQLServer應用的階段特別有用。

     

  • 中間件設計有配置系統,支持任一主要查詢維度可按配置精準的將數據源定向到SQLServer或MySQL,並可控制是否讀取後加載到訂單緩存。初期設定只加載SQLServer數據源,避免因雙庫間的數據不一致而造成緩存數據跳躍。並在初期可設置灰度,將小批量非核心表直連MySQL驗證可靠性。後期數據一致性達成預期後,訂單緩存也可自由按指定數據庫加載緩存。

     

  • 解決了查詢場景下的數據一致性問題後,流量策略支持圖3.3中任一可調控維度進行數據庫單寫。實際項目中以表維度實施單寫爲主,當指定表被配置單寫MySQL後,所有涉及該表的CRUD行爲全部定向MySQL,包括緩存加載源。

     

  • 最後通過中間件統一收口對外發送的訂單消息,所有消息基於中間件的CUD操作發送與物理數據庫無關,這樣實現消息的數據源透明,且可聯動以上所有工藝操作,數據鏈保持一致。

                                                            圖3.3  操作工藝簡介

 

3、數倉銜接

爲了方便理解生產數據到數據倉庫ODS層數據的遷移,做到對下游透明,這裏簡單介紹一下常規數據倉庫的分層體系。通常數據倉庫主要分爲五層:ODS(原始數據層)、DIM(維度)、EDW(企業數倉)、CDM(通用模型層)、ADM(應用模型層),如下圖所示:

 

                         圖3.4  數據倉庫分層結構

從圖3.4上可以看出,數據倉庫各層都依賴ODS層的數據,爲了不影響數據平臺所有應用,我們只需要將原來訂單庫ODS層數據源從SQLServer遷移到MySQL庫即可。

 

從圖上很直觀的看出,遷移只需換個數據源不是很麻煩,但是爲了保證數據質量,我們做了很多的前置工作,比如:DBA預先將生產數據同步到生產MySQL庫、MySQL數據實時同步、生產兩側數據一致性校驗、MySQL側數據同步到ODS層、ODS層數據一致性校驗及原有ODS層同步Job數據源切換等。

 

其中,生產兩側數據一致性校驗和數據倉庫ODS層數據一致性校驗最爲複雜,耗時也最長,要確保每張表、每個字段都要一致時才能切換數據源。但是,從實際操作過程中,卻做不到完全一致。根據實際情況,適當處理時間類型、浮點值精度及小數位等。

 

下面介紹一下整體流程:

 

首先,對於線上數據一致校驗,我們開發了在線同步Job,將SQLServer的數據和MySQL數據進行比較,發現不一致時,就將MySQL的數據以SQLServer數據爲基準更新掉,確保兩邊數據的一致性。

 

其次,對於離線數據一致性校驗,我們和數據倉庫同事合作把MySQL側數據同步到ODS層(以庫名區分是SQLServer還是MySQL的表),並且將定時跑的任務和SQLServer側任務在時間上儘量一致。兩側數據都準備好後,我們開發了離線數據校驗腳本生成器,根據數據倉庫元數據,爲每張表生成一個同步Job,將其部署到調度平臺。

 

同步任務會依賴兩側ODS層同步數據,T+1數據同步完成後,執行一致性校驗,將不一致的訂單號記錄到不一致明細表中,並統計不一致的數據量,將結果保存到統計表中。然後在自助報表平臺製作一個報表,將每天統計的不一致的表及不一致量發送到郵箱,我們每天對不一致的表進行排查找出問題,調整比較策略,更新比較Job。大致流程如下:

                                                           圖3.5  一致性校驗整體流程

 

最後,隨着線上和離線數據逐步趨於一致後,我們將原先SQLServer同步到ODS層Job的數據源切換到MySQL。這裏可能有同學會有疑問:爲什麼不直接使用MySQL側ODS層的表呢?原因是,經過統計,依賴原先ODS層表的Job有上千個之多,如果讓依賴Job切換到MySQL側ODS表,修改工作量非常大,所以我們直接將原來的ODS層同步數據源直接切換成MySQL。

 

實際操作中,切數據源並不能一次全部切完,我們分三批進行,先找十幾個不那麼重要的表作爲第一批,切完後運行兩週,並收集下游數據問題的反饋。第一批表順利切完兩週後,我們沒收到下游報數據問題,說明數據質量沒問題。然後再將剩餘的幾百張表按重要程度分兩批繼續切,直到切完。

 

至此,我們完成了訂單庫從SQLServer遷移到MySQL在數據倉庫層的遷移工作。

 

 

四、核心問題精編

實際上再周密的分析與設計,總是難免遇到執行過程中的各種挑戰。我們總結了一些經典問題,雖然通過技術手段最終解決了這些大大小小問題並達成了目標,但是相信各位看官必定還有更好的解決方案,我們樂見共同學習與進步。

 

1、SQLServer & MySQL 流量遷移如何細粒度監控

訂單系統涉及到的應用和表數量衆多,一個應用對應1到n張表,一張表又對應1到n個應用,是典型的多對多關係。如圖4.1,對於上層應用來說,從一個SQLServer數據庫,切換到另一個MySQL數據庫,其基本流程參照操作工藝章節至少分爲以下幾步:

 

  • 從單寫SQLServer變成雙寫SQLServer和MySQL

     

  • 從單讀SQLServer變成單讀MySQL

     

  • 從雙寫SQLServer和MySQL變成單寫MySQL

     

  • 下線SQLServer

 

                                                                                        圖4.1 應用和數據庫以及表的關係圖

在生產環境更換數據庫系統,就像在高速公路上不停車換輪胎,需要維持原有的車速不變,且對用戶無感,否則後果不敢設想。

 

在切換工藝中雙寫、單讀和單寫流程,環環相扣,步步相依,作爲配套設計監控手段必須確認上一個操作達到預期效果才能進行下一個。如果跳過或者沒有切換乾淨就貿然進行下一步,比如還沒有雙寫完全一致,就開始讀MySQL數據,可能造成查無此數據或者查到髒數據!那麼就需要對每一個CRUD操作的讀寫進行監控,在遷移過程中做到360度無死角可視化流量細分控制,所見即所得。具體的做法如下:

 

  • 所有應用接入中間件,CRUD由中間件根據配置控制讀寫哪個DB的哪張表;

     

  • 每一個讀寫操作的詳細信息均寫入ES,在Kibana和Grafana上可視化展示,並且通過DBTrace,可以知道每條SQL是在哪個DB上執行;

     

  • 按照應用級別逐步配置雙寫DB,通過同步Job實時比對、修復和記錄兩側DB差異,再通過離線T+1校驗雙寫中出現的最終不一致,如此往復直到雙寫一致;

     

  • 雙寫一致之後,就開始逐步將讀SQLServer切換到讀MySQL,通過ES監控和DBTrace確認完全沒有SQLServer讀,則表明單讀MySQL完成,考慮到自增主鍵情況,我們採取按照表維度,按批次斷寫SQLServer,直至所有表都單寫MySQL。

 

綜上所述,基本方案爲通過中間件爲管道爲所有接入的應用統一埋點,通過實時展示應用層的行爲觀察流量分佈,並結合公司數據庫側Trace的可視化工具覈實應用的流量切換行爲與數據庫實際QPS及負載浮動保持一致來監督遷移任務。

 

2、如何解決雙寫期間DB一致性問題

酒店的訂單庫有着二十年左右歷史,經年累積,跨部門和酒店內部多個團隊直接或間接依賴訂單庫SQLServer,要想切換到MySQL,就得先解決雙寫DB一致性問題,不一致主要體現在以下兩點:

 

  • 雙寫時實際僅單寫了SQLServer,漏寫MySQL;

     

  • 雙寫SQLServer和MySQL成功,併發、不可靠網絡、GC等發生時MySQL數據有機率和SQLServer不一致。

 

關於雙寫數據一致性的保證,我們基於同步Job將SQLServer數據爲準線,根據最後更新時間,拉取兩側DB數據進行比對,如果不一致則修復MySQL的數據並將不一致信息寫入ES,供後續排查根因。

 

但也因爲引入了額外的Job操作MySQL數據,帶來了新的問題,那就是多表雙寫時,因爲耗時翻倍,Job發現SQLServer有數據而MySQL沒有,就立即修復了MySQL數據,造成雙寫失敗。所以雙寫部分失敗又加上了Failover機制,通過拋送消息,觸發新一輪的比對和修復工作,直到兩側DB數據完全一致。

 

同步Job和Failover消息機制雖然可以讓數據最終一致,但畢竟有秒級的間隔,兩側數據是不一致的,並且對於衆多應用的各種場景,難免會有遺漏時單寫SQLServer。對於這些漏寫MySQL的地方,通過DBTrace是無法找到的,因爲無法確定一個CUD操作只寫入SQLServer,而未寫入MySQL。那麼有沒有辦法事前就能找出漏寫MySQL的場景呢,確實被我們找出來一點,那就是更換數據庫連接串,接入中間件的應用使用新連接串,然後找出所有使用舊連接串操作SQLServer的SQL,就能準確定位出漏寫MySQL的流量了。

 

最終,我們將雙寫DB不一致率從十萬分之二逐步降低到了幾乎爲0,爲什麼是幾乎呢,因爲DB的一些特性差異問題,會天然的導致數據無法完全一致,這個在後續內容會有詳細的論述。

 

3、引入訂單緩存後導致的數據不同步問題處理

引入緩存之後,就涉及到對緩存進行寫入或者更新,業界常見的做法分爲以下幾種:

 

  • 先寫DB再寫緩存

  • 先寫緩存再寫DB

  • 先刪緩存再寫DB

  • 先寫DB再刪緩存

 

在具體實施上還會進行雙刪緩存或者延遲雙刪緩存,此處不再比較各種做法的優劣。我們採用的是先寫DB再刪緩存方案,對於數據敏感表,會進行延遲雙刪,後臺的同步Job定時比對、修復和記錄數據庫數據與Redis數據的差異,雖然設計上已經能保證最終一致性,但是在前期還是出現過大量的數據不一致。主要體現在以下幾個方面:

 

  • 應用有場景未接入中間件,對DB進行CUD操作之後,漏刪除緩存;

     

  • 寫DB後刪除緩存延遲導致讀取到緩存髒數據,比如不可靠網絡、GC等造成刪緩存延遲;

     

  • 寫DB後刪除緩存失敗導致讀取到緩存髒數據,比如Redis主從切換期間,只能讀不可寫。

 

而爲了解決緩存一致性問題,如圖4.2,我們在原有的緩存和DB基礎上,增加了樂觀鎖和CUD施工標記,來限制併發情況下同時存在加載數據到緩存相互覆蓋的行爲,以及對當前被查數據正在進行CUD操作的感知。在此兩種場景未結束期間可以做到Query流量直連DB,通過基於樂觀鎖的最後寫入者獲勝機制解決競爭問題。最終我們的緩存不一致率從百萬分之二控制到了千萬分之三。

 

                                                                    圖4.2 緩存一致性解決

圖4.2當查詢未命中緩存,或當前存在該數據的樂觀鎖或施工標記時,當次查詢直連DB,直至相關事務完成後放開緩存數據自動加載功能。

 

4、存量訂單數據如何一次性校準

 

項目啓動初期我們對MySQL進行了最近N年數據的一次性鋪底,這就產生了在雙寫階段無法校準的如下兩個場景的數據:

 

  • 因生產上訂單庫預置保留近N年的數據,負責清理備份的Job在接入中間件前,MySQL已存在的N年外的這批數據無法被策略覆蓋而清理掉。

     

  • 所有應用接中間件花了很長時間,接中間件雙寫前數據有可能不一致的,需要全部應用接中間件和全部表雙寫後,對之前的數據進行一次性修復。

 

針對第一點,我們開發了MySQL數據專項清理Job,由於訂單數據庫是多Shard的,Job內部根據實際Shard數設置核心線程總量,每個線程分別負責對應Shard中的指定表進行清理,並行開多臺服務器分發任務進行清理,通過速度控制既保證了效率又不影響生產上數據庫的負載。

 

針對第二點,在所有應用接中間件和所有表實現雙寫後,通過調整線上同步Job掃描的開始時間戳,對存量訂單數據進行修復。修復時特別注意的是,掃描數據要按時間段分片處理,防止加載數據太多導致訂單庫服務器CPU太高。

 

5、一些數據庫特性差異問題

在如此龐大的系統下進行數據庫熱遷移,我們就必須瞭解不同數據庫之間的差異與不同,做到知己知彼,對症下藥。MySQL與SQLServer雖同爲時下流行的關係型數據庫,均支持標準化SQL查詢,但在細枝末節上還是有些許差異。下面我們通過遷移中所面臨的問題來具體分析一下。

 

1)自增鍵問題

爲保證數據自增序號一致,不能讓兩個數據庫各自去進行自增,否則一旦不一致就要面臨修數據甚至更大風險。因此,在數據雙寫時,我們將SQLServer寫入後生成的自增id,回寫入MySQL自增列,在數據單寫MySQL時直接使用MySQL生成自增id值。

 

2)日期精度問題

雙寫後爲了保證數據一致性,要對兩側數據進行一致性校驗,類型爲Date、DateTime、Timestamp的字段,由於保存精度不一致,在對比時就需要做特殊處理,截取到秒進行比較。

 

3)XML字段問題

SQLServer中支持XML數據類型,而MySQL 5.7不支持XML類型。在使用varchar(4000)代替後,遇到MySQL數據寫入失敗,但同步Job將SQLServer數據回寫MySQL時又能正常寫入的案例。經過分析,程序在寫入時會將未壓縮的XML字符串寫入,SQLServer XML類型會自動壓縮並存儲,但MySQL並不會,導致長度超過4000的寫入操作失敗,SQLServer壓縮後長度小於4000,又能夠正常回寫MySQL。爲此我們提出應對措施,寫入前壓縮並校驗長度,非重要字段截取後再存儲,重要字段優化存儲結構或更換字段類型。

 

下面列舉一些遷移過程中常見的注意點。

 

 

五、預警實踐

我們的預警實踐並不侷限於項目推進期間的監控訴求,如何在百億級數據中週期掃描數據寫入的異常,完成項目期間雙寫數據一致率的複覈,如何實時監控與預警訂單庫每個分片上訂單寫入量的正常趨勢,如何定期驗收/覈驗整套系統的高可用性將在以下篇幅中描述。

 

1、百億級數據差異校驗預警

要滿足訂單數據SQLServer遷移到MySQL庫,數據質量是遷移的必要條件,數據一致性達不到要求就無法透明遷移,所以設計合理的校驗方案,關乎遷移的進度。針對數據校驗,我們分爲線上和線下兩種:

 

  • 線上數據校驗和預警

 

遷移期間我們通過同步Job,在計算出不一致數據後,將不一致的表及字段寫入ElasticSearch,再用Kibana製作出不一致數據量及不一致表所佔比例的監控看板,通過監控看板,我們就可以實時監控哪些表數據不一致量比較高,再根據表名稱通過DBA工具排查出哪些應用對錶進行了CUD操作,進一步定位漏接中間件的應用和代碼。

 

在實際操作中,我們確實找出了大量未接中間的應用並對其改造,隨着接入中間件的應用越來越多,數據一致性逐漸提高,從監控看板上看到不一致的量也慢慢降低。但是一致性始終沒有降低到零,原因是應用和同步Job併發導致的,這個也是最令人頭疼的問題。

 

或許有同學會疑問,既然雙寫了爲什麼不停止掉同步Job呢?原因是雙寫以SQLServer爲主寫,以受中間件覆蓋的CUD範圍爲基準,除了不能保證寫入MySQL的數據百分百成功外也不能保證兩庫的數據量相等,所以需要一致性Job兜底。由於併發的存在,雖然做不到數據百分百一致,但是可以進一步降低。

 

我們的做法是,一致性Job比較時設置一個5秒的穩定線(即距離當前時間5秒內的數據視爲不穩定數據),訂單數據時間戳在穩定線內的不進行比較,穩定線外的比較時,會再一次計算訂單數據是否在穩定線內,如果確認全部數據在穩定線外,就進行比較操作,否則放棄本次比較,由下一次調度執行一致性校驗。

 

  • 離線數據校驗和預警

 

訂單庫遷移涉及到幾百張表,離線數據比較多,一年的訂單相關數據就有上百億了,對於離線數據校驗比較有挑戰。我們編寫了數據一致性腳本生成器,爲每張表生成一個比較腳本並部署到調度平臺,比較腳本依賴上游SQLServer和MySQL兩側的同步Job,上游Job執行完畢後自動執行數據比較,將不一致數據的訂單號寫到明細表中,並根據明細表統計出不一致量,以日報的形式發出,每天對數據不一致比較高的表排查並解決。

 

通常一是能修復對比腳本的瑕疵,二是發現離線數據問題,就這樣反覆摸排解決不一致問題。對於離線數據每張表每個字段的校驗是非常複雜的,我們編寫UDF函數進行比較,UDF函數功能也很簡單,就是將每張表的非主鍵字段進行拼接生成一個新字段,兩側表進行全外連接,主鍵或者邏輯主鍵相等的記錄,生成新字段也應該一樣,只要不一樣就視爲不一致數據。這裏要注意日期字段截取、數據精度及末尾爲零的小數處理問題。

 

經過三個多月的努力,我們排查出所有未接中間件的應用,並將其CUD操作全部接入中間件,開啓雙寫後線上線下數據一致性逐步提高,達到了遷移數據的目標。

 

2、ALL Shard 實時訂單總量監控  

每個公司對於訂單量的監控是不可或缺的,攜程有一個統一預警平臺Sitemon,它主要監控各類訂單告警,包括酒店,機票,無線,高鐵,度假。並能按照online/offline,國內/國際,或者支付方式單獨搜索和展現,並對各類訂單做了告警。

 

訂單數據從SQLServer遷移到MySQL期間,我們梳理出來依賴訂單庫的預警策略近兩百個,負責監控的相關同事對SQL Server數據源的預警策略原樣複製一份連接MySQL數據源。以MySQL爲數據源監控告警都添加完成後,開啓報警策略,一旦訂單量異常報警,NOC會收到兩條通知,一條來源於SQLServer數據告警,一條來源於MySQL告警,如果兩邊一致,說明灰度驗證通過。否則,不通過,需排查MySQL 監控問題。

 

經過一段時間的灰度驗證,兩邊報警數據一致,隨着SQLServer數據表下線(即單寫MySQL數據),以SQLServer爲數據源的預警策略也跟着及時下線。

 

3、“流浪地球”實操

爲了做好系統安全保障工作,提高應對突發事件的能力,必要的演練壓測等是少不了的。爲此,我們制定了完備的應急預案並定期組織開展應急演練——流浪地球。演練項目包括核心/非核心應用熔斷、DB熔斷、Redis熔斷、核心防火牆、交換機應急切換等。

 

以緩存爲例,爲了保證緩存服務的高可用,我們在演練時會下線部分節點或機器甚至切斷整個Redis服務,模擬緩存雪崩、緩存擊穿等場景。按照計劃,在熔斷前我們會先切斷應用的Redis訪問,一步步降低Redis負載,然後熔斷Redis,以此檢驗在無Redis的情況下各應用系統是否能夠正常運轉。

 

但在首次演練中,熔斷Redis後應用報錯量就急劇上升,果斷停止演練回退並查找原因。經過分析,部分應用Redis操作未統一收口,不受中間件統一控制,Redis熔斷後應用隨即出現異常。針對這一情況,我們分析後一方面將報錯應用的訂單緩存訪問收口接入中間件,另一方面強化了中間件與Redis的弱依賴關係,支持一鍵斷開Redis操作,並完善了各項指標監控。最終在第二次演練中順利完成Redis熔斷,各業務系統在全流量打入MySQL的狀態下的正常運行。在最近一次的流浪地球演練中,機房網絡阻斷、非核心應用阻斷等一輪輪故障注入後,我們的系統更是取得了很好的預期效果。

 

就這樣,在一次次的演練中,我們發現問題,總結經驗,優化系統,完善應急預案,一步步提升系統應對突發故障的能力,保證業務的連續性以及數據的完整性。做好底層數據支撐,爲整個酒店訂單系統保駕護航。

 

六、未來規劃

1、訂單緩存手工調控臺

 

雖然我們有完善的監控看板與預警系統,但對於像熔斷演練、自動化故障演練、硬件故障和維護以及不可提前預知的問題,若剛好核心開發人員未能及時在現場響應操作,系統尚不能完全自主降級可能導致部分性能有所下降,比如響應耗時增加等。在將來計劃增加手工調控看板,授權後可以讓NOC或者TS進行鍼對性操作,比如Redis全部或者部分集羣宕機,可以一鍵切割故障Redis分片,或者根據Redis已計劃中的不可用時間段來提前設置切割時間,可以最大程度保證系統的可控性。

 

2、中間件自動降級

 

既然可以手工進行調控,那麼我們也考慮後續可以通過一些核心指標的監控,比如Redis主從切換期間,正常情況是秒級,但是我們也出現過部分Redis 10秒以上不可寫的情況,此時可以監控緩存與數據庫不一致的髒數據量,也可以在Redis發生故障時通過監控響應耗時異常的閥值來應用一些策略,讓中間件自動降級切割掉這些故障主機保證服務的基本穩定,然後在探測到集羣指標穩定後再逐步嘗試恢復。

 

3、中間件接入Service Mesh

 

當前訂單團隊內部是以JAR的方式使用中間件,由中間件來屏蔽數據庫底層差異和操作Redis以實現更復雜的功能,天然具備接入Service Mesh能力,接入後底層升級更加快速和無感、調用更加輕量化、更好與框架進行網格化集成以及上雲更加方便,能夠更好的支撐攜程的國際化戰略目標。

 

 

作者丨榮華、軍威、金永、俊強

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章