“ 對任何公司來說賬務都是一種關鍵服務,這一點大部分人都不會否認。在任何遷移項目中,數據庫的遷移都是最基本要素,數據庫能否成功遷移直接決定了整個項目能否成功。
CDE(云數據庫工程)團隊最近對這一最重要的數據庫子系統進行了遷移。本次項目的目標在于將所有這一切都搬入云中,不在數據中心內運行任何計費應用程序或數據庫,但所有操作不能影響業務的正常運轉。我們的前路十分艱巨!
前言
毫無疑問,在不中斷業務的情況下遷移高度敏感的應用程序和重要數據庫是一項意義深遠的工作,與此同時我們還將繼續構建新的業務功能和服務。
計費系統的一些重要用途和面臨的挑戰包括:
當我們著手進行該項目時,計費系統是這樣的:
規劃
我們制訂了一個三步規劃:
從每個國家遷移過程的每一步操作中學習經驗,進行迭代和完善,確保后續工作能取得更好的成績。
第1步 – 將新落地國家重定向至云中,將數據同步回數據中心
很快將在6個新國家落地。我們決定利用這一機會直接通過云環境運行這些國家的部分計費系統。這意味著面向用戶的數據和應用程序將從云中運行,但依然需要將數據同步回數據中心,這樣數據中心內的批處理應用程序才能繼續運行,不至于影響到業務運轉。這些新落地國家客戶的數據將保存在云中,但批處理任務依然在數據中心內處理。這是我們的第一步。
我們將2個面向用戶的應用程序中的所有API移植到使用 Boot和 開發的云應用程序中。通過使用 Boot可以快速著手創建新應用程序,這個產品內建了開發工作所需的基礎結構和組件,可以讓我們更專注業務邏輯本身。
通過使用 ,只需一次開發碼即可重復使用大部分工作流風格的代碼。借助這些產品對以及基于的路由技術提供的支持,我們可以在應用程序內部實現Pub-sub模式,將消息放入一個渠道(),并讓每個用戶通過各自獨立的方式使用。
在將數據存儲于的情況下,現在可以通過任意AWS區域處理這6個新國家會員的API調用。就算某個AWS區域徹底故障,這些國家的計費操作也不會受到影響,而這也是我們首次真正意義上認識到云計算的威力!
我們在AWS多個區域的EC2實例上部署了自己的應用程序,另外為現有的云代理應用程序增加了一個重定向層,以便將新落地國家用戶的計費調用切換至云中新部署的計費API,并讓原有國家用戶的計費調用繼續由數據中心內原有的計費API處理。
我們從一個AWS區域建立了到數據中心內現有數據庫的直接連接,并開發了一個程序,通過SQS將另外3個區域中的數據與這個建立了直接連接的區域進行同步。我們還使用SQS隊列和Dead (DLQ)在故障的區域和過程之間移動數據。
在新國家落地通常意味著會員數量的激增。我們也明白,為了確保數據中心不超載,還必須將現有的續訂應用程序從數據中心搬入云中。因此對于通過云服務運行的6個新落地國家,我們編寫了一個爬蟲程序,可以每天一次遍歷中的所有客戶,借此找出所有當天需要收費的會員。
這種“逐行迭代”的方法目前在這些國家很好用,但我們也清楚,在將其他國家,尤其是美國(目前我們的絕大部分會員都在美國)的數據遷移到云中之后這種方式將會失效。但我們想先行一步試試水有多深。這也是目前這一階段唯一在云中運行的批處理應用程序。
為了能夠在任何一個區域執行寫操作,并將寫操作快速復制到其他區域,我們選擇用作為數據存儲。我們定義了一種數據模型,在其中使用作為行,并創建了一系列復合的列借此體現數據之間的關系性。下圖展示了這些項之間的關系,以及我們是如何在中使用單列族( )進行體現的。用單列族形式設計這樣的關系使得我們能為相關項提供事務支持。
通過對應用程序的邏輯進行設計,只需要在任何操作開始執行時讀取一次,隨后即可在內存中更新對象,并在操作結束后將其以單列族的形式持久存儲。在操作過程中讀取或寫入的操作會被看作一種反模式(Anti-)。我們使用(自行開發并已開源的客戶端)編寫了自定義的ORM,這樣就可以針對讀/寫域對象。
我們通過這種方式將服務落地到新的國家,雖然遇到了幾個小問題,但在迅速修復后整個系統運轉很穩定。目前來說一切都挺不錯的!
經過第1步工作后計費系統的體系結構如下圖所示:
第2步 – 遷移所有應用程序,并將原有國家遷移至云中
第1步成功完成后,我們開始考慮在不遷移數據庫的情況下將其他應用遷至云中。大部分業務邏輯位于批處理應用程序中,多年來已經發展得極為成熟,但這也意味著必須深入到每個條件的代碼中并花費大量時間重寫。這些應用程序無法“照原樣”直接搬到云中運行。
同時我們也借助這次機會盡量移除了所有不再使用的代碼,將不同功能拆分為多個專用的小應用程序,并為了更好的擴展性重構了現有代碼。這些遺留應用程序被我們設計為會在啟動時讀取磁盤上的配置文件,并使用了其他一些靜態資源,例如從隊列讀取消息,由于實例與生俱來的“短暫”本質,這些特征在云環境中都是反模式的。
因此為了讓應用程序在云平臺上順利運行,只能重新實現這些模塊。為了通過異步模式將消息穿過隊列移動到不同區域,我們還更改了一些API,并在這些區域建立了到數據中心的安全連接。
云數據庫工程團隊為我們的數據需求搭建了多節點集群。我們也清楚,在將所有會員的計費數據遷移到之后,以前用來為最早的6個國家的客戶提供續訂服務所用的“全行(Row)式”迭代器續訂解決方案將無法很好地伸縮。
因此我們使用設計了一個系統數據庫不所需保存更改,可從 拉取數據并將其轉換為JSON格式的行,將其暫存在S3 中。隨后我們寫了一些Pig腳本,借此每天針對大量數據集運行作業數據庫不所需保存更改,找出需要續訂的客戶清單并向他們收費。
我們還寫了Sqoop作業以便從和中拉取數據,并將其以可查詢格式寫入Hive,這樣就可以將這兩套數據集匯總至Hive,實現更快速的排錯。
為了讓DVD服務器能夠連接云環境,我們為DVD設置了負載平衡端點(包含SSL客戶端證書),DVD服務器可以通過云代理對所有調用進行路由,在遷移美國系統之前可以借此將調用重新發回數據中心。美國系統的數據遷移完成后,即可斷開云和數據中心之間的通信鏈路。
為了對這一大規模數據遷移的結果進行驗證,我們編寫了對已遷往云中的數據,以及數據中心內部現有數據進行比較和驗證的對比工具。反復運行該對比工具可找出遷移過程中可能存在的Bug,修復發現的問題,清理數據并再次運行。
隨著運行結果愈發整潔,完全沒有出現任何錯誤,這也進一步增強了我們對數據遷移工作的信心。對于針對各國數據進行的遷移工作我們感到十分激動。最開始我們選擇了一個會員數量比較少的國家,并通過下列步驟將數據遷入云中:
在確認一切正常后開始遷移下一個國家。隨后我們開始突擊對所有類似國家一起進行遷移。最后遷移的是美國,因為美國的會員數量最多,并且還提供有DVD訂閱服務。至此所有面向會員客戶的數據都已通過云環境提供。對我們來說這是一個巨大的里程碑!
經過第2步工作后,我們的體系結構如下圖所示:
第3步 – 再見,數據中心!
至此還有最后(并且最重要)的一件事:數據中心內運行的數據庫。中存儲的數據集具有高度的關系性,我們覺得這些數據并不適合以NoSQL的方式進行建模。
這種數據不能像以前處理面向客戶的訂閱數據那樣構造為單列族的形式,因此我們評估了和 RDS這兩種選項。但作為云數據庫運行的許可成本,以及依然是Beta測試版這一現狀使得則兩種方式都不適合我們。
在計費團隊忙于執行前兩個步驟時,我們的云數據庫工程團隊正在創建用于將計費數據遷移至EC2上MySQL實例所需的基礎結構。在開始執行第三步操作時,在他們的幫助下數據庫基礎結構部分已經就緒。
因為一些應用程序使用了不包含任何ORM的純JDBC,我們還需要將批處理應用程序的代碼基轉換為兼容MySQL的格式。另外我們處理了大量遺留的Pl-sql代碼,并重寫了應用程序中的邏輯,同時盡可能去除了不再使用的代碼。
至此我們的數據庫體系結構已經由部署在某一AWS區域內EC2實例上的MySQL主數據庫組成。我們還搭建了一個從主數據庫復制數據的災難恢復數據庫,如果主數據庫故障,該數據庫將成為新的主數據。另外我們還在在其他AWS區域設置了從數據庫,這些數據庫主要為應用程序提供只讀訪問。
至此我們的計費系統已經全部搬入云中,現在看起來是下面這個樣子:
數據庫的遷移過程
接下來將深入介紹數據庫的遷移過程。希望我們的經驗能幫你順利完成自己的遷移任務。
你是否考慮過為了順利完成復雜的數據庫遷移任務,都需要考慮并解決哪些問題?但你可能也會問,“這有什么復雜的?”
想想數據庫遷移過程中遇到的下列挑戰吧,我們本次遷移幾乎遇到了所有這些問題:
在任何遷移項目中,數據庫的遷移都是最基本要素,數據庫能否成功遷移直接決定了整個項目能否成功。下文將介紹為確保遷移項目成功完成所采取的一些關鍵措施。
數據庫的選擇
為順利處理付款過程中產生的事務,計費應用程序的事務須符合ACID(原子性、一致性、隔離性、持久性)要求。RDBMS似乎是此類數據存儲的最佳選擇。
:由于源數據庫使用了產品,直接遷移至云中運行的數據庫可避免進行跨數據庫遷移,降低代碼開發和配置工作量。我們過去在生產環境中使用產品的體驗也讓自己對該產品的性能和伸縮性更有信心。然而考慮到許可成本以及“依原樣”遷移遺留數據所要產生的技術債,最終只能尋求其他解決方案。
AWS RDS MySQL:理想情況下我們會選擇作為后端,畢竟亞馬遜在關系型數據庫即服務產品的管理和升級方面做的挺好,為了實現高可用還提供了多可用區(AZ)支持。然而RDS的主要不足之處在于存儲容量有著6TB上限。我們遷移時的容量已接近10TB。
AWS :AWS 可以滿足我們對存儲容量的需求,但目前還是Beta測試版。
:是一種強大的對象-關系開源數據庫系統,但我們團隊內部缺乏足夠的使用經驗。在自己的數據中心內我們主要使用和MySQL作為后端數據庫,更重要的是選擇會導致未來無法無縫遷移至,因為使用了基于MySQL的引擎。
EC2 MySQL:最終我們的計費系統選擇使用EC2 MySQL,這種技術無須許可成本,同時未來可以直接遷移至。該方式需要在i2.實例上使用引擎配置MySQL。
生產數據庫體系結構
為確保計費應用程序可以承受基礎結構、區域和地域故障,并將可能的停機時間降至最低,高可用性和伸縮性是我們設計整個體系結構時最主要的考慮因素。
通過在另一個區域內為數據庫主副本創建DRBD副本,即可承受區域故障,節點出錯等基礎結構故障,以及EBS卷故障。當本地和遠程寫操作均完成后,會使用“同步復制協議”將主要節點上的寫操作標記為已完成。借此可確保一個節點的故障絕對不會導致數據丟失。雖然這樣的設計會影響寫操作的延遲,但延遲依然在SLA可接受的范圍內。
讀取副本可設置為本地或跨區域配置,這樣不僅可以滿足對高可用的需求,而且有助于增強伸縮性。來自ETL作業的讀取流量會分流至讀取副本,借此降低主要數據庫執行繁重ETL批處理的負擔。
一旦主要MySQL數據庫故障,工作負載將被故障轉移至使用同步模式進行復制的DRBD輔助節點。輔助節點開始承擔主節點的角色后,會更改數據庫主機的 DNS記錄將其指向新的主節點。按照設計,計費應用程序與生俱來的“批處理”特性可順利應對此類停機事件。CNAME記錄傳播工作完成后,客戶端連接不會回退(),而是會建立指向新主節點的連接。
遷移工具的選擇
我們在遷移工具的選擇方面花費了大量時間和精力。概念驗證工作成功與否的最主要條件在于能否重啟動批載荷(Bulk load)、雙向復制,以及數據完整性。在評估遷移工具時我們主要側重于下列幾個條件。
以豐富的功能脫穎而出,該產品很好地滿足了我們的需求。可以在遇到故障后重啟動批載荷(很少的幾張表就達到數百GB容量),該產品的雙向復制功能可以讓我們從MySQL輕松回滾到。
的主要不足在于了解該工具工作原理所面臨的學習曲線。此外該產品使用了易于出錯的手工配置過程,這也增大了項目難度。如果源表沒有主鍵或唯一鍵,會使用所有列作為提取和復制操作的增補日志鍵對。但我們發現了一些問題,例如復制到目標的數據僅僅是相關表的增量載荷,因此決定在切換這些表的過程中執行不預定義主鍵或唯一鍵的完整加載。的優勢和包含的功能遠遠超過了所造成的困難,我們最終選擇使用該工具。
架構轉換和驗證
由于源和目標數據庫存在差異,數據類型和長度也有所不同,為了在遷移數據的同時確保數據完整性,驗證工作變得必不可少。
數據類型誤配造成的問題需要花些時間來修復。例如因為一些歷史遺留原因,中的很多數值已定義為數據類型,MySQL缺少類似的類型。中的數據類型會存儲定數和浮點數,這一點比較難以處理。
一些源表中的列使用代表整數,另一些情況則會代表十進制數值,其中一些值的長度甚至達到38位。作為對比,MySQL使用了明確的數據類型,例如Int、、、等,而不能超過18位。因此必須確保通過恰當的映射以便在MySQL中反應精確的值。
分區表( table)需要特殊處理,與的做法不同,MySQL會將分區鍵視作主鍵和唯一鍵的一部分。為確保不對應用邏輯和查詢產生影響,必須用恰當的分區鍵重新定義目標架構。
默認值的處理在MySQL和之間也有不同。對于包含NOT NULL值的列,MySQL會確定該列暗含的默認值,在MySQL中啟用模式即可看到此類數據轉換問題,這樣的事務會執行失敗并顯示在的錯誤日志中。
架構轉換工具:為了實現架構轉換并進行驗證,我們評估了多種工具,但由于原有架構設計中所存在的問題,這些工具默認提供的架構轉換功能無法使用。即使也無法將架構轉換為相應的MySQL版本,因此只能首先由應用程序的所有者重新定義架構。
優化架構也是我們此次遷移的目標之一,數據庫和應用程序團隊合作審閱了數據類型,并通過多次迭代找出了所有誤配的內容。在存在誤配的情況下,會對這些值進行截斷以符合MySQL數據類型的要求。問了緩解這一問題,我們主要借助數據對比工具和錯誤日志找出源和目標之間數據類型的誤配。
數據完整性
完整加載和增量加載執行完畢后,又遇到另一個讓人氣餒的問題:必須核實目標副本的數據完整性。由于和MySQL使用了不同數據類型,無法通過用普通封裝腳本對比行鍵()哈希值的方式保證數據的精確性。
雖然有幾個第三方工具能跨越不同數據庫對實際值進行數據對比,但總量10TB的數據集比較起來也不容易。最終我們使用這些工具對比了樣本數據集,借此找出了少數由于架構映射錯誤導致的不一致問題。
測試刷新:確保數據完整性的方法之一是使用應用程序對生產數據庫的副本進行測試。為此可安排從MySQL生產數據庫進行刷新并用于測試。考慮到生產環境使用EBS作為存儲,只要創建EBS快照即可輕松創建測試環境,同時可在測試中執行時間點恢復。為確保足夠高的數據質量,這一過程重復了多次。
Sqoop作業:我們在數據校正過程中使用了ETL作業和報表,并使用Sqoop作業從中拉取創建報表所需的數據。此外還針對MySQL配置了這些作業。在源和目標之間進行持續復制的過程中,會在ETL的特定時間窗口內運行報表,這樣即可找出增量加載過程中產生的變化。
行計數(Row count)是用于對源/目標進行比較和匹配的另一種方法。為此需要首先暫停目標的增量加載,并對和MySQL的行數進行匹配。在使用完整加載表之后也會對行計數的結果進行比較。
性能調優
基礎結構:計費應用程序將數據持久保存在數據中心內兩個數據庫中,運行數據庫的計算機性能極為強大,使用了IBM Power 7,32顆雙核心64位處理器,750GB內存,通過SVC MCS集群分配TB級別的存儲,集群使用了4GB/s接口,運行配置的8G4集群。
遷移過程中最大的顧慮是性能,目標數據庫將整合到一個裝備有32顆vCPU和244GB內存的i2.服務器上。為了優化查詢性能,應用程序團隊在應用層進行了大量調優。在的幫助下,性能團隊可以方便地發現性能瓶頸,通過調整特定的系統和內核參數解決這些問題。詳細信息請參閱附件。
我們用EBS供應的IOPS卷組建RAID0實現了極高的讀寫性能。為了通過每個卷獲得更高吞吐率,共使用5個容量各4TB的卷,而沒有使用更大容量的單個卷。這樣做也可以加快創建快照和還原的速度。
數據庫:對于MySQL的使用我們還有一個比較大的顧慮,擔心計費應用程序在對數據執行批處理過程中MySQL的吞吐率無法滿足數據規模的需求。為此提供了顧問支持,在遷移過程中以及遷移之后,MySQL數據庫的性能表現都讓我們感到滿意。
這里的訣竅在于使用兩個cnf文件,一個用于遷移數據的過程中對之類的參數進行優化,以便執行批量插入;第二個cnf文件用于在實時生產應用程序工作負載中對之類的參數進行調整,借此促進事務的實時加載。詳情請參閱附件。
數據加載:在概念驗證過程中,我們針對開啟和關閉索引兩種情況測試了表的初始加載,并決定在加載前啟用所有索引。這樣做的原因在于MySQL中索引是通過單線程方式創建的(大部分表有多個索引),因此我們改為使用的并行加載功能在合理的時間內為表中填入索引。最后一次割接過程中還啟用了外鍵約束。
我們學到的另一個竅門是按照實例的內核數量執行相同遍數的完整和增量加載過程。如果這些過程的執行遍數超過內核數量,數據加載性能將大幅降低,因為實例需要花費更多時間進行上下文切換。通過完整加載和增量加載將10TB數據裝入目標MySQL數據庫,這一過程用了大約兩周時間。
結論
雖然對任何遷移項目來說,數據庫的遷移都是最大挑戰,但真正決定項目成功與否的關鍵在于要確保一開始就選擇了正確的方法,并且在整個執行過程中與應用程序團隊密切合作。
回顧整個遷移過程,這個項目的成功完全是組織內部不同團隊通力合作的成果,大家一起制定的整個遷移計劃是促成這一切的關鍵!為了在不影響業務的前提下順利完成整個充滿挑戰的遷移項目,除了人員和團隊之間的相互協調,自由的文化和責任感也是促成這一切必不可少的要素。
附件
批量插入時對數據庫的調節
高事務吞吐率的數據庫調節
存儲
CPU調度器方面的調節
虛擬機的調節
文件系統和IO存儲指標
▽
延展閱讀(點擊標題):
喜歡我們的會點贊,愛我們的會分享!