拍攝紙牌屋的Netflix為何要遷移資料庫入雲?
Netflix CDE(雲資料庫工程)團隊最近對這一最重要的資料庫子系統進行了遷移。本次專案的目標在於將所有這一切都搬入雲中,不在資料中心內執行任何計費應用程式或資料庫,但所有操作不能影響業務的正常運轉。我們的前路十分艱鉅!
前言
毫無疑問,在不中斷業務的情況下遷移高度敏感的應用程式和重要資料庫是一項意義深遠的工作,與此同時我們還將繼續構建新的業務功能和服務。
計費系統的一些重要用途和麵臨的挑戰包括:
- 計費團隊負責管理整個公司的重要財務資料。我們每天會通過使用者的付費訂閱、禮品卡、信用額度、退款等行為生成大量資料,這些資料將彙總至財務部門並建立成報表交給公司會計。為確保每天的收入情況可以準確記錄,我們在日常處理流程中實施了嚴格的SLA。處理管線的任何延遲都是無法接受的。
- 計費系統對資料丟失持零容忍態度。
- 大部分情況下,現有資料使用了一種關係型模式的結構,因此需要通過事務確保這類資料實現“全有或全無(all-or-nothing)”的行為。換句話說,我們的運維必須符合ACID(原子性、一致性、隔離性、永續性)要求。但某些情況下還必須讓這些資料實現跨區域高可用,同時確保不同區域間複製的延遲最小化。
- 計費系統與公司的DVD業務相整合,而DVD業務與線上流播業務使用了截然不同的體系結構,這也大幅增加了整合工作的複雜度。
- 支付團隊還希望向Netflix客戶服務代理提供資料支援,幫助他們回答會員提出的有關計費操作的問題。因此迫切需要向客戶支援人員提供此類資料的概括性檢視。
當我們著手進行該專案時,計費系統是這樣的:
- 資料中心內部署2個Oracle資料庫 – 一個儲存客戶訂閱資訊,另一個儲存發票/支付資料。
- 多個基於REST的應用程式 – 為www.netflix.com和客戶支援應用程式的呼叫提供服務。這些應用程式主要執行CRUD(建立、讀取、更新、刪除)操作。
- 3個批處理應用程式:
- 服務續訂 – 這個每天執行一次的作業會掃描所有客戶資訊以確定當天需要計費的客戶,並通過這些客戶的訂閱計劃、折扣等資訊確定需要計費的金額。
- 訂單和支付處理 – 通過一系列批處理作業為需要續訂的客戶建立發票,並負責在發票生命週期內的不同階段處理有關發票的任務。
- 營收報表 – 這個每天執行一次的作業會檢索計費資料並生成Netflix財務部門需要的報表。
- 一個計費代理應用程式(位於雲中) – 用於將Netflix在雲中的其他應用程式的呼叫路由至資料中心。
- 使用老版本格式的Weblogic佇列負責不同過程之間的通訊。
規劃
我們制訂了一個三步規劃:
- 第1步 – 服務新落地國家的計費系統直接從雲中執行,並將所產生的資料同步回資料中心,供原有批處理應用程式繼續使用。
- 第2步 – 對面向用戶的資料進行建模,以實現最終一致性並且不再需要符合ACID特性,將這些資料持久儲存在Cassandra(Cassandra使得我們能夠在一個區域執行寫操作,並用非常低的延遲讓寫入的資料可在所有區域使用。同時還可以幫助我們實現跨區域高可用性)。
- 第3步 – 最終將SQL資料庫遷移至雲中。
從每個國家遷移過程的每一步操作中學習經驗,進行迭代和完善,確保後續工作能取得更好的成績。
第1步 – 將新落地國家重定向至雲中,將資料同步回資料中心
Netflix很快將在6個新國家落地。我們決定利用這一機會直接通過雲環境執行這些國家的部分計費系統。這意味著面向使用者的資料和應用程式將從雲中執行,但依然需要將資料同步回資料中心,這樣資料中心內的批處理應用程式才能繼續執行,不至於影響到業務運轉。這些新落地國家客戶的資料將儲存在雲中,但批處理任務依然在資料中心內處理。這是我們的第一步。
我們將2個面向使用者的應用程式中的所有API移植到使用Spring Boot和Spring Integration開發的雲應用程式中。通過使用Spring Boot可以快速著手建立新應用程式,這個產品內建了開發工作所需的基礎結構和元件,可以讓我們更專注業務邏輯本身。
通過使用Spring Integration,只需一次開發碼即可重複使用大部分工作流風格的程式碼。藉助這些產品對Header以及基於Header的路由技術提供的支援,我們可以在應用程式內部實現Pub-sub模式,將訊息放入一個渠道(Channel),並讓每個使用者通過各自獨立的方式使用。
在將資料儲存於Cassandra的情況下,現在可以通過任意AWS區域處理這6個新國家會員的API呼叫。就算某個AWS區域徹底故障,這些國家的計費操作也不會受到影響,而這也是我們首次真正意義上認識到雲端計算的威力!
我們在AWS多個區域的EC2例項上部署了自己的應用程式,另外為現有的雲代理應用程式增加了一個重定向層,以便將新落地國家使用者的計費呼叫切換至雲中新部署的計費API,並讓原有國家使用者的計費呼叫繼續由資料中心內原有的計費API處理。
我們從一個AWS區域建立了到資料中心內現有Oracle資料庫的直接連線,並開發了一個程式,通過SQS將另外3個區域中的Cassandra資料與這個建立了直接連線的區域進行同步。我們還使用SQS佇列和Dead Letter Queues(DLQ)在故障的區域和過程之間移動資料。
在新國家落地通常意味著會員數量的激增。我們也明白,為了確保資料中心不超載,還必須將現有的續訂應用程式從資料中心搬入雲中。因此對於通過雲服務執行的6個新落地國家,我們編寫了一個爬蟲程式,可以每天一次遍歷Cassandra中的所有客戶,藉此找出所有當天需要收費的會員。
這種“逐行迭代”的方法目前在這些國家很好用,但我們也清楚,在將其他國家,尤其是美國(目前我們的絕大部分會員都在美國)的資料遷移到雲中之後這種方式將會失效。但我們想先行一步試試水有多深。這也是目前這一階段唯一在雲中執行的批處理應用程式。
為了能夠在任何一個區域執行寫操作,並將寫操作快速複製到其他區域,我們選擇用Cassandra作為資料儲存。我們定義了一種資料模型,在其中使用customerId作為行,並建立了一系列複合的Cassandra列藉此體現資料之間的關係性。下圖展示了這些項之間的關係,以及我們是如何在Cassandra中使用單列族(Single column family)進行體現的。用單列族形式設計這樣的關係使得我們能為相關項提供事務支援。
通過對應用程式的邏輯進行設計,只需要在任何操作開始執行時讀取一次,隨後即可在記憶體中更新物件,並在操作結束後將其以單列族的形式持久儲存。在操作過程中讀取或寫入Cassandra的操作會被看作一種反模式(Anti-pattern)。我們使用Astyanax(Netflix自行開發並已開源的Cassandra客戶端)編寫了自定義的ORM,這樣就可以針對Cassandra讀/寫域物件。
我們通過這種方式將服務落地到新的國家,雖然遇到了幾個小問題,但在迅速修復後整個系統運轉很穩定。目前來說一切都挺不錯的!
經過第1步工作後計費系統的體系結構如下圖所示:
第2步 – 遷移所有應用程式,並將原有國家遷移至雲中
第1步成功完成後,我們開始考慮在不遷移資料庫的情況下將其他應用遷至雲中。大部分業務邏輯位於批處理應用程式中,多年來已經發展得極為成熟,但這也意味著必須深入到每個條件的程式碼中並花費大量時間重寫。這些應用程式無法“照原樣”直接搬到雲中執行。
同時我們也藉助這次機會盡量移除了所有不再使用的程式碼,將不同功能拆分為多個專用的小應用程式,併為了更好的擴充套件性重構了現有程式碼。這些遺留應用程式被我們設計為會在啟動時讀取磁碟上的配置檔案,並使用了其他一些靜態資源,例如從Weblogic佇列讀取訊息,由於例項與生俱來的“短暫”本質,這些特徵在雲環境中都是反模式的。
因此為了讓應用程式在雲平臺上順利執行,只能重新實現這些模組。為了通過非同步模式將訊息穿過佇列移動到不同區域,我們還更改了一些API,並在這些區域建立了到資料中心的安全連線。
雲資料庫工程團隊為我們的資料需求搭建了多節點Cassandra叢集。我們也清楚,在將所有Netflix會員的計費資料遷移到Cassandra之後,以前用來為最早的6個國家的客戶提供續訂服務所用的“全行(Row)式”Cassandra迭代器續訂解決方案將無法很好地伸縮。
因此我們使用Aegisthus設計了一個系統,可從Cassandra SSTable拉取資料並將其轉換為JSON格式的行,將其暫存在S3 Bucket中。隨後我們寫了一些Pig指令碼,藉此每天針對大量資料集執行Mapreduce作業,找出需要續訂的客戶清單並向他們收費。
我們還寫了Sqoop作業以便從Cassandra和Oracle中拉取資料,並將其以可查詢格式寫入Hive,這樣就可以將這兩套資料集彙總至Hive,實現更快速的排錯。
為了讓DVD伺服器能夠連線雲環境,我們為DVD設定了負載平衡端點(包含SSL客戶端證書),DVD伺服器可以通過雲代理對所有呼叫進行路由,在遷移美國系統之前可以藉此將呼叫重新發回資料中心。美國系統的資料遷移完成後,即可斷開雲和資料中心之間的通訊鏈路。
為了對這一大規模資料遷移的結果進行驗證,我們編寫了對已遷往雲中的資料,以及資料中心內部現有資料進行比較和驗證的對比工具。反覆執行該對比工具可找出遷移過程中可能存在的Bug,修復發現的問題,清理資料並再次執行。
隨著執行結果愈發整潔,完全沒有出現任何錯誤,這也進一步增強了我們對資料遷移工作的信心。對於針對各國資料進行的遷移工作我們感到十分激動。最開始我們選擇了一個Netflix會員數量比較少的國家,並通過下列步驟將資料遷入雲中:
- 禁用待遷移國家的非GET API(該操作不會影響會員服務,但可能導致計費系統中訂閱更新工作延遲)。
- 使用Sqoop作業將資料從Oracle轉移至S3和Hive。
- 使用Pig將其轉換為Cassandra格式。
- 將該國家所有會員的記錄插入Cassandra。
- 啟用非GET API,通過雲平臺為被遷移國家的使用者提供資料。
在確認一切正常後開始遷移下一個國家。隨後我們開始突擊對所有類似國家一起進行遷移。最後遷移的是美國,因為美國的會員數量最多,並且還提供有DVD訂閱服務。至此所有面向Netflix會員客戶的資料都已通過雲環境提供。對我們來說這是一個巨大的里程碑!
經過第2步工作後,我們的體系結構如下圖所示:
第3步 – 再見,資料中心!
至此還有最後(並且最重要)的一件事:資料中心內執行的Oracle資料庫。Oracle中儲存的資料集具有高度的關係性,我們覺得這些資料並不適合以NoSQL的方式進行建模。
這種資料不能像以前處理面向客戶的訂閱資料那樣構造為單列族的形式,因此我們評估了Oracle和Aurora RDS這兩種選項。但Oracle作為雲資料庫執行的許可成本,以及Aurora依然是Beta測試版這一現狀使得則兩種方式都不適合我們。
在計費團隊忙於執行前兩個步驟時,我們的雲資料庫工程團隊正在建立用於將計費資料遷移至EC2上MySQL例項所需的基礎結構。在開始執行第三步操作時,在他們的幫助下資料庫基礎結構部分已經就緒。
因為一些應用程式使用了不包含任何ORM的純JDBC,我們還需要將批處理應用程式的程式碼基轉換為相容MySQL的格式。另外我們處理了大量遺留的Pl-sql程式碼,並重寫了應用程式中的邏輯,同時儘可能去除了不再使用的程式碼。
至此我們的資料庫體系結構已經由部署在某一AWS區域內EC2例項上的MySQL主資料庫組成。我們還搭建了一個從主資料庫複製資料的災難恢復資料庫,如果主資料庫故障,該資料庫將成為新的主資料。另外我們還在在其他AWS區域設定了從資料庫,這些資料庫主要為應用程式提供只讀訪問。
至此我們的計費系統已經全部搬入雲中,現在看起來是下面這個樣子:
資料庫的遷移過程
你是否考慮過為了順利完成複雜的資料庫遷移任務,都需要考慮並解決哪些問題?但你可能也會問,“這有什麼複雜的?”
想想資料庫遷移過程中遇到的下列挑戰吧,我們本次遷移幾乎遇到了所有這些問題:
- 源和目標硬體存在差異;
- 使用了不同的作業系統;
- 需要跨域異構資料庫進行遷移;
- 涉及多個數據中心 – Netflix資料中心(DC)和AWS雲;
- 要遷移的是非常關鍵的交易計費資料;
- 有選擇地遷移資料集;
- 需要在最小停機時間的前提下遷移持續變化的資料。
在任何遷移專案中,資料庫的遷移都是最基本要素,資料庫能否成功遷移直接決定了整個專案能否成功。下文將介紹為確保遷移專案成功完成所採取的一些關鍵措施。
資料庫的選擇
為順利處理付款過程中產生的事務,計費應用程式的事務須符合ACID(原子性、一致性、隔離性、永續性)要求。RDBMS似乎是此類資料儲存的最佳選擇。
Oracle:由於源資料庫使用了Oracle產品,直接遷移至雲中執行的Oracle資料庫可避免進行跨資料庫遷移,降低程式碼開發和配置工作量。我們過去在生產環境中使用Oracle產品的體驗也讓自己對該產品的效能和伸縮性更有信心。然而考慮到許可成本以及“依原樣”遷移遺留資料所要產生的技術債,最終只能尋求其他解決方案。
AWS RDS MySQL:理想情況下我們會選擇MySQL RDS作為後端,畢竟亞馬遜在關係型資料庫即服務產品的管理和升級方面做的挺好,為了實現高可用還提供了多可用區(AZ)支援。然而RDS的主要不足之處在於儲存容量有著6TB上限。我們遷移時的容量已接近10TB。
AWS Aurora:AWS Aurora可以滿足我們對儲存容量的需求,但目前還是Beta測試版。
PostgreSQL:PostgreSQL是一種強大的物件-關係開源資料庫系統,但我們團隊內部缺乏足夠的PostgreSQL使用經驗。在自己的資料中心內我們主要使用Oracle和MySQL作為後端資料庫,更重要的是選擇PostgreSQL會導致未來無法無縫遷移至Aurora,因為Aurora使用了基於MySQL的引擎。
EC2 MySQL:最終我們的計費系統選擇使用EC2 MySQL,這種技術無須許可成本,同時未來可以直接遷移至Aurora。該方式需要在i2.8xlarge例項上使用InnoDB引擎配置MySQL。
生產資料庫體系結構
為確保計費應用程式可以承受基礎結構、區域和地域故障,並將可能的停機時間降至最低,高可用性和伸縮性是我們設計整個體系結構時最主要的考慮因素。
通過在另一個區域內為資料庫主副本建立DRBD副本,即可承受區域故障,節點出錯等基礎結構故障,以及EBS卷故障。當本地和遠端寫操作均完成後,會使用“同步複製協議”將主要節點上的寫操作標記為已完成。藉此可確保一個節點的故障絕對不會導致資料丟失。雖然這樣的設計會影響寫操作的延遲,但延遲依然在SLA可接受的範圍內。
讀取副本可設定為本地或跨區域配置,這樣不僅可以滿足對高可用的需求,而且有助於增強伸縮性。來自ETL作業的讀取流量會分流至讀取副本,藉此降低主要資料庫執行繁重ETL批處理的負擔。
一旦主要MySQL資料庫故障,工作負載將被故障轉移至使用同步模式進行復制的DRBD輔助節點。輔助節點開始承擔主節點的角色後,會更改資料庫主機的route53 DNS記錄將其指向新的主節點。按照設計,計費應用程式與生俱來的“批處理”特性可順利應對此類停機事件。CNAME記錄傳播工作完成後,客戶端連線不會回退(Fallback),而是會建立指向新主節點的連線。
遷移工具的選擇
我們在遷移工具的選擇方面花費了大量時間和精力。概念驗證工作成功與否的最主要條件在於能否重啟動批載荷(Bulk load)、雙向複製,以及資料完整性。在評估遷移工具時我們主要側重於下列幾個條件。
- 批/增量載荷的重啟動;
- 雙向複製;
- 每個表並行性(Parallelism per table);
- 資料完整性;
- 傳輸過程中錯誤報告;
- 上線後回滾的能力;
- 效能;
- 易用性。
GoldenGate以豐富的功能脫穎而出,該產品很好地滿足了我們的需求。GoldenGate可以在遇到故障後重啟動批載荷(很少的幾張表就達到數百GB容量),該產品的雙向複製功能可以讓我們從MySQL輕鬆回滾到Oracle。
GoldenGate的主要不足在於瞭解該工具工作原理所面臨的學習曲線。此外該產品使用了易於出錯的手工配置過程,這也增大了專案難度。如果源表沒有主鍵或唯一鍵,GoldenGate會使用所有列作為提取和複製操作的增補日誌鍵對。但我們發現了一些問題,例如複製到目標的資料僅僅是相關表的增量載荷,因此決定在切換這些表的過程中執行不預定義主鍵或唯一鍵的完整載入。GoldenGate的優勢和包含的功能遠遠超過了所造成的困難,我們最終選擇使用該工具。
架構轉換和驗證
由於源和目標資料庫存在差異,資料型別和長度也有所不同,為了在遷移資料的同時確保資料完整性,驗證工作變得必不可少。
資料型別誤配造成的問題需要花些時間來修復。例如因為一些歷史遺留原因,Oracle中的很多數值已定義為Number資料型別,MySQL缺少類似的型別。Oracle中的Number資料型別會儲存定數和浮點數,這一點比較難以處理。
一些源表中的列使用Number代表整數,另一些情況則會代表十進位制數值,其中一些值的長度甚至達到38位。作為對比,MySQL使用了明確的資料型別,例如Int、bigInt、decimal、double等,而bigInt不能超過18位。因此必須確保通過恰當的對映以便在MySQL中反應精確的值。
分割槽表(Partitioned table)需要特殊處理,與Oracle的做法不同,MySQL會將分割槽鍵視作主鍵和唯一鍵的一部分。為確保不對應用邏輯和查詢產生影響,必須用恰當的分割槽鍵重新定義目標架構。
預設值的處理在MySQL和Oracle之間也有不同。對於包含NOT NULL值的列,MySQL會確定該列暗含的預設值,在MySQL中啟用Strict模式即可看到此類資料轉換問題,這樣的事務會執行失敗並顯示在GoldenGate的錯誤日誌中。
架構轉換工具:為了實現架構轉換並進行驗證,我們評估了多種工具,但由於原有架構設計中所存在的問題,這些工具預設提供的架構轉換功能無法使用。即使GoldenGate也無法將Oracle架構轉換為相應的MySQL版本,因此只能首先由應用程式的所有者重新定義架構。
優化架構也是我們此次遷移的目標之一,資料庫和應用程式團隊合作審閱了資料型別,並通過多次迭代找出了所有誤配的內容。在存在誤配的情況下,GoldenGate會對這些值進行截斷以符合MySQL資料型別的要求。問了緩解這一問題,我們主要藉助資料對比工具和GoldenGate錯誤日誌找出源和目標之間資料型別的誤配。
資料完整性
完整載入和增量載入執行完畢後,又遇到另一個讓人氣餒的問題:必須核實目標副本的資料完整性。由於Oracle和MySQL使用了不同資料型別,無法通過用普通封裝指令碼對比行鍵(Rowkey)雜湊值的方式保證資料的精確性。
雖然有幾個第三方工具能跨越不同資料庫對實際值進行資料對比,但總量10TB的資料集比較起來也不容易。最終我們使用這些工具對比了樣本資料集,藉此找出了少數由於架構對映錯誤導致的不一致問題。
測試重新整理:確保資料完整性的方法之一是使用應用程式對生產資料庫的副本進行測試。為此可安排從MySQL生產資料庫進行重新整理並用於測試。考慮到生產環境使用EBS作為儲存,只要建立EBS快照即可輕鬆建立測試環境,同時可在測試中執行時間點恢復。為確保足夠高的資料質量,這一過程重複了多次。
Sqoop作業:我們在資料校正過程中使用了ETL作業和報表,並使用Sqoop作業從Oracle中拉取建立報表所需的資料。此外還針對MySQL配置了這些作業。在源和目標之間進行持續複製的過程中,會在ETL的特定時間視窗內執行報表,這樣即可找出增量載入過程中產生的變化。
行計數(Row count)是用於對源/目標進行比較和匹配的另一種方法。為此需要首先暫停目標的增量載入,並對Oracle和MySQL的行數進行匹配。在使用GoldenGate完整載入表之後也會對行計數的結果進行比較。
效能調優
基礎結構:計費應用程式將資料持久儲存在資料中心內兩個Oracle資料庫中,執行資料庫的計算機效能極為強大,使用了IBM Power 7,32顆雙核心64位處理器,750GB記憶體,通過SVC MCS叢集分配TB級別的儲存,叢集使用了4GB/s介面,執行RAID10配置的8G4叢集。
遷移過程中最大的顧慮是效能,目標資料庫將整合到一個裝備有32顆vCPU和244GB記憶體的i2.8xlarge伺服器上。為了優化查詢效能,應用程式團隊在應用層進行了大量調優。在Vector的幫助下,效能團隊可以方便地發現效能瓶頸,通過調整特定的系統和核心引數解決這些問題。詳細資訊請參閱附件。
我們用EBS供應的IOPS卷組建RAID0實現了極高的讀寫效能。為了通過每個卷獲得更高吞吐率,共使用5個容量各4TB的卷,而沒有使用更大容量的單個卷。這樣做也可以加快建立快照和還原的速度。
資料庫:對於MySQL的使用我們還有一個比較大的顧慮,擔心計費應用程式在對資料執行批處理過程中MySQL的吞吐率無法滿足資料規模的需求。Percona為此提供了顧問支援,在遷移過程中以及遷移之後,MySQL資料庫的效能表現都讓我們感到滿意。
這裡的訣竅在於使用兩個cnf檔案,一個用於遷移資料的過程中對innodb_log_file_size之類的引數進行優化,以便執行批量插入;第二個cnf檔案用於在實時生產應用程式工作負載中對innodb_buffer_pool_instances之類的引數進行調整,藉此促進事務的實時載入。詳情請參閱附件。
資料載入:在概念驗證過程中,我們針對開啟和關閉索引兩種情況測試了表的初始載入,並決定在載入前啟用所有索引。這樣做的原因在於MySQL中索引是通過單執行緒方式建立的(大部分表有多個索引),因此我們改為使用GoldenGate的並行載入功能在合理的時間內為表中填入索引。最後一次割接過程中還啟用了外來鍵約束。
我們學到的另一個竅門是按照例項的核心數量執行相同遍數的完整和增量載入過程。如果這些過程的執行遍數超過核心數量,資料載入效能將大幅降低,因為例項需要花費更多時間進行上下文切換。通過完整載入和增量載入將10TB資料裝入目標MySQL資料庫,這一過程用了大約兩週時間。
結論
回顧整個遷移過程,這個專案的成功完全是組織內部不同團隊通力合作的成果,大家一起制定的整個遷移計劃是促成這一切的關鍵!為了在不影響業務的前提下順利完成整個充滿挑戰的遷移專案,除了人員和團隊之間的相互協調,自由的文化和責任感也是促成這一切必不可少的要素。
附件
批量插入時對資料庫的調節
高事務吞吐率的資料庫調節
儲存
- 使用5個4TB EBS PIOPS卷組建RAID0
- 使用LVM管理同一卷組中的兩個邏輯卷(DB和DRBD元資料)
CPU排程器方面的調節
虛擬機器的調節
檔案系統和IO儲存指標
文章出處:InfoQ
本文翻譯已獲授權有刪節,原文地址:
http://techblog.netflix.com/2016/07/netflix-billing-migration-to-aws-part-ii.html
http://techblog.netflix.com/2016/08/netflix-billing-migration-to-aws-part.html
本文譯者:大愚若智