自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

大型系統(tǒng)存儲層遷移實踐

原創(chuàng) 精選
存儲 數(shù)據(jù)管理
本文從存儲層遷移流程的角度詳細闡述了大型系統(tǒng)存儲遷移的過程,分析了其中的難點和過程中的一些坑,總結來說過程中也有一些不足和感悟。

背景

作為一個以新聞、資訊為主的 App,今日頭條上的主要內容都是由文章組成,文章服務自然伴隨著今日頭條 App 的產(chǎn)生就已出現(xiàn),之后又逐步擴展為目前的內容云,為頭條、西瓜、小說、懂車帝等多個 App 服務的業(yè)務內容中臺。截止 2021 年底,內容云接入子業(yè)務已經(jīng)達到數(shù)百個,高峰期主要讀服務 QPS 數(shù)百萬,維護超過 2200 個屬性,存量數(shù)據(jù)達到百億條級別。然而由于歷史悠久,經(jīng)手人眾多,加上歷史上一些環(huán)境或周邊系統(tǒng)的特殊性,業(yè)務模式發(fā)生轉變等,使得內容云成為一個標準的大型遺留系統(tǒng),早期的一些存儲、架構上的設計已經(jīng)逐漸無法滿足當前的業(yè)務場景,并給維護者帶來了較大維護和迭代成本。

因此我們啟動了內容云存儲層的遷移項目,隨著調研和與其他業(yè)務的討論的不斷深入,發(fā)現(xiàn)各業(yè)務對存儲層的痛點及需求基本一致,存儲模型和實現(xiàn)方案逐漸趨同,因此決定基于 ByteKV 開發(fā)一個寬表數(shù)據(jù)服務(本文主要聚焦在遺留系統(tǒng)存儲層遷移的過程,暫不涉及新存儲層的設計與實現(xiàn)細節(jié)),下沉存儲層通用邏輯,供其他業(yè)務接入,并替換內容云原有的存儲層。最終歷時將近 1 年時間將在線流量切換至新的存儲層。

遷移一個系統(tǒng)的存儲能有多復雜?無非是雙寫、遷移數(shù)據(jù)、切讀、停寫罷了,為何內容云存儲層的遷移竟花費將近一年時間?本文主要分享內容云存儲層遷移的血淚史,過程中的一些坑和經(jīng)驗,望能給其他大型系統(tǒng)遷移存儲或做重構帶來一些流程上的參考。

名詞解釋

  • 文章服務,內容云:字節(jié)跳動內部提供內容全生命周期解決方案的內容業(yè)務中臺。
  • ByteKV:字節(jié)跳動內部自研強一致 KV 模型存儲組件。
  • ABase:字節(jié)跳動內部自研高可用 KV 模型存儲組件。
  • 寬表數(shù)據(jù)服務:新的存儲層,通用的表格模型數(shù)據(jù)服務,通過下沉存儲層的通用能力,減少重復建設,降低維護成本,提升研發(fā)效率。

難點

領域邊界調整

雖然大體目標是將原存儲層替換成新的存儲層,但預期本次遷移也需要解決原存儲層由來已久的多存儲不一致問題、容量瓶頸、主從延遲等問題,這要求在遷移過程中也需要對內容云業(yè)務層進行大量改造,將原有業(yè)務層中包含的存儲層功能下沉到新的存儲層,使業(yè)務層和存儲層邊界明確,帶來了額外的工作量。

數(shù)據(jù)模型變更

由于原有主要存儲為 MySQL,本身數(shù)據(jù)模型為表格型,而新存儲使用 ByteKV,數(shù)據(jù)模型為 KV 模型,雖然在新存儲層建設過程中已經(jīng)完成了基于 KV 模型提供表格模型能力的開發(fā),但相關功能的能力與舊存儲層的能力仍有偏差,需要在遷移過程中不斷的完善和進一步改造。

數(shù)據(jù)量、請求量大

遷移時內容云數(shù)據(jù)量已經(jīng)達到數(shù)百億條,主要讀服務請求高峰期流量數(shù)百萬 QPS,大的數(shù)據(jù)量+大請求量使得在雙寫、做 diff、刷數(shù)據(jù)等每個階段都需要考慮性能問題,資源問題。本身雙寫雙讀期間就需要引入額外的資源消耗,使得過程中不得不抽出一些時間優(yōu)化之前系統(tǒng)的性能,以釋放出一些資源進行雙寫、雙讀、消 diff 及壓測等驗證工作。后面會詳細介紹兩次大的性能優(yōu)化過程。

迭代中遷移

唯一不變的是變化,在整個遷移過程中內容云系統(tǒng)也在持續(xù)進行迭代,整個遷移的過程如同給正在奔跑的汽車換輪胎,給正在飛行的飛機換發(fā)動機,需要做到業(yè)務無感。新 feture 的加入需要同時作用到兩套存儲上,否則就會產(chǎn)生 diff,時刻關注 diff 情況并追齊新加的 feature 同樣花費了不少時間。

歷史包袱眾多

由于業(yè)務經(jīng)手人數(shù)較多,歷史悠久,遺留系統(tǒng)中都有眾多黑盒及不可解釋的邏輯,對這些邏輯的理解及兼容是前期項目計劃之外的。此外歷史數(shù)據(jù)的混亂,已經(jīng)無法用現(xiàn)有系統(tǒng)的標準去度量,為保證切換過程中透明,甚至需要去兼容歷史上錯誤的數(shù)據(jù)。

痛點分析

內容云本身對存儲層的依賴如下圖:

此架構主要有以下幾個問題:

  • 存儲組件使用 2 個 MySQL+2 個 ABase 協(xié)同提供服務,但業(yè)務上操作四個存儲很難保證事務性,即使做一些補償也很難保證四個存儲同時成功或同時失敗,導致產(chǎn)生較多的多存儲不一致問題。
  • 對于一些存儲層的通用能力,如加密、版本、審計、緩存等與業(yè)務層沒有明顯邊界,相應邏輯揉雜在業(yè)務代碼中,對業(yè)務代碼侵入較強。
  • 主要存儲為 MySQL,原生 MySQL 并非存儲、計算分離的架構,在大數(shù)據(jù)量的業(yè)務上存儲容量常常成為瓶頸,只能不斷進行拆庫"續(xù)命"。
  • 大多數(shù)上層業(yè)務對數(shù)據(jù)一致性要求較高,MySQL 的主從延遲的抖動會造成緩存中存在臟數(shù)據(jù),引發(fā)數(shù)據(jù)不一致。
  • MySQL 中單列存儲容量存在上限,導致業(yè)務上對于一些"大"文章的存儲需求無法滿足。

過程

前置代碼準備

此階段主要進行數(shù)據(jù)雙寫代碼準備,及寫 diff 流程、監(jiān)控的搭建。

從上述存儲架構可以看出,上層業(yè)務統(tǒng)一通過了抽象接口層(data_source)訪問底層的存儲,理論上在抽象接口層新增一個寬表數(shù)據(jù)服務的實現(xiàn),并把舊存儲的實現(xiàn)直接替換為新存儲的實現(xiàn)即可完成存儲的替換,即基于新存儲的依賴如下圖所示:

然而,理想很美好,現(xiàn)實總是很骨感,這樣雖然能做到替換存儲,但并沒有達到重構的目的,即解決多存儲不一致等問題,之前分別處理多個存儲的代碼在業(yè)務層進行,通過 data_source 中的不同接口進行不同存儲數(shù)據(jù)的操作,因此需要進行 data_source 接口的改造和在線寫服務中操作多存儲部分的代碼改造。同時把寫 diff 的流程搭建起來。

此階段主要開發(fā)工作有:

  • 存儲抽象接口層(data_source)的接口改造,使得可以通過一次請求替代之前操作存儲的多個請求。
  • 在線寫服務操作多存儲的邏輯下沉,在業(yè)務層不再感知到存儲層相關的邏輯。
  • 開啟雙寫,把新存儲作為弱依賴雙寫數(shù)據(jù)。
  • 基于寫數(shù)據(jù)事件觸發(fā) diff 服務,搭建寫 diff 流程(收到事件重新讀取兩存儲中的數(shù)據(jù),并比較進行打點監(jiān)控)。
  • 進行壓測。資源總是緊張的,需要預先申請,此時的新存儲集群只能夠承擔雙寫的流量,此階段需要進行初步壓測并預估最終所需資源數(shù)量并提交申請。

寫 diff 消除

寫 diff 過程不管做的多細致都不過分

上階段代碼準備完成后,開始無盡的消 diff 工作,由于內容云字段已經(jīng)超過 2000 個,需要對有 diff 的字段逐個進行排查,并不斷進行代碼改造以消除這些 diff,是一個極度需要細致和耐心的過程。

最終寫 diff 消除用時 1 個月左右。后面也證明寫 diff 階段不管多細致都不過分,因為寫 diff 消除完成證明了數(shù)據(jù)寫入已經(jīng)沒有問題了,可以進行歷史數(shù)據(jù)遷移,如果歷史數(shù)據(jù)遷移完后又發(fā)現(xiàn)有寫 diff,很可能需要再次全量刷一遍數(shù)據(jù),費時費力。然而雖然用時一個月后來發(fā)現(xiàn)仍有一些坑,導致大大小小最終刷了不下 10 遍數(shù)據(jù),后面說。

下面總結下比較有代表性的寫 diff:

1.自身邏輯實現(xiàn)的 bug 及新流程未與舊流程完全對齊(這類導致的 diff 其實是最多的,具體要看本身的業(yè)務邏輯,沒什么參考意義,只能不斷的去追平邏輯再驗證)。

2.舊存儲特性導致的 diff,有默認值。即業(yè)務上沒有寫對應數(shù)據(jù),但舊存儲 MySQL 每個列可以配置默認值。

3.舊存儲本身配置不合理導致的 diff,如:

  • 字符集配置的 UTF-8,導致本身存儲中不支持 emoji 表情,而新存儲中支持導致的 diff。
  • 字段類型配置為 tinyint,導致業(yè)務上如果寫一個較大的值時會發(fā)生溢出,而新存儲不會。

4.兩個存儲一個成功、一個失敗導致的 diff,需要在一個存儲失敗時進行后續(xù)的補償重試,因此搭建了數(shù)據(jù)修復流程,期望兩存儲能夠達到最終一致的狀態(tài)。

5.請求亂序,如下圖,可能會發(fā)生請求 2 比請求 1 先到的情況。需要在寫請求之前加鎖,并在兩存儲寫完后再釋放鎖,前提是能確保新存儲的性能不會對上游產(chǎn)生影響。

6.時間戳問題

由于兩存儲無法保證準確的同一時刻寫入,導致有些時間戳會出現(xiàn) diff,這種解決方案分兩種情況,對于無法接受 diff 的時間戳需要在業(yè)務層統(tǒng)一時間戳,再指定使用統(tǒng)一時間戳寫入兩存儲。對于能夠接受 diff 的時間戳需要在 diff 時忽略掉。

7.序列化問題

一些反序列化方法會把 JSON 中的數(shù)字轉為 json.Number,這在業(yè)務中類型斷言或 diff 比較時都會留坑,應盡量在下層處理好這類問題。

8.序列化的順序

由于 map 結構的無序性,在序列化成字符串時會導致順序不一致,可能在某些業(yè)務邏輯中有坑,較好的方法是在序列化時保證進行有序的序列化,已經(jīng)有許多開源的 JSON 庫能夠做這樣的事情。

9.服務本身的異步寫入

這種 diff 可能是內容云獨有的,之前有較多邏輯直接在寫服務寫完主存儲后,起異步協(xié)程再進行一些計算和數(shù)據(jù)操作,這使得這些寫入的請求順序無法得到保證。較好的做法是把操作存儲的邏輯收斂到統(tǒng)一的寫服務接口上。

10.存儲一前一后寫入,或一前一后讀取導致的 diff

由于無法保證在做 diff 時的事務隔離性(會影響在線服務,不太能接受),會存在在 diff 讀取時剛好有并發(fā)的數(shù)據(jù)寫入操作,導致的不一致,這種即使延遲一段時間再次進行 diff 也無法完全消除,因此最終 diff 的消除也無法達到 100%的一致率,最終在一致率達到 99.99%時經(jīng)追查仍有 diff 的 case,發(fā)現(xiàn)都屬于這種情況,這時認為寫 diff 已經(jīng)消除完成了。

歷史數(shù)據(jù)遷移

嘗試探索更高效的歷史數(shù)據(jù)遷移方案能提升存儲遷移的效率,除非能保證只刷一遍數(shù)據(jù)

經(jīng)過寫 diff 消除階段,此時理論上新增的數(shù)據(jù)寫入已經(jīng)沒有問題了(只是理論上,后面讀 diff 時發(fā)現(xiàn)還是有一些邊緣 case 導致的寫 diff)。這個階段主要是把歷史存量數(shù)據(jù)從舊存儲導入新存儲中。這個過程依然基于統(tǒng)一接口層 data_source 實現(xiàn)。

這個階段同樣需要做完備 diff,需要驗證導入的歷史數(shù)據(jù)是否符合預期,需要進行歷史數(shù)據(jù)的正確性校驗,但當時由于新存儲本身資源不足,離線數(shù)據(jù)也還不支持產(chǎn)出,此時進行歷史上 400 億條數(shù)據(jù)的對比是無法進行的,因此這個階段只進行了有明顯問題 diff 的修復,把歷史數(shù)據(jù) diff 的校驗工作放到了切讀前的最后一步,但更合理的做法是在此時就校驗好歷史數(shù)據(jù)的正確性,否則之后可能會產(chǎn)生重復的刷數(shù)據(jù)工作。

此階段主要會遇到的問題是如果一些數(shù)據(jù)是在真實數(shù)據(jù)寫入時生成的,可能有問題,需要新存儲支持這些數(shù)據(jù)可以指定寫入,如:

  • create_time 類數(shù)據(jù),是在新數(shù)據(jù)寫入時根據(jù)時間戳生成的,但歷史數(shù)據(jù)的 create_time 不能使用刷數(shù)據(jù)時的時間,因此需要新存儲支持上游指定寫入 create_time 的值,進行一些代碼改造。

刷數(shù)據(jù)的工作主要是依賴業(yè)務上層的實現(xiàn)進行,因此刷數(shù)據(jù)的過程需要進行大量的計算邏輯,是比較低效的,理論上把刷數(shù)據(jù)的工作越下沉越高效,比如參考 MySQL 遷移數(shù)據(jù)時的文件級別拷貝等。由于當時考慮內容云遷移本身 1. 數(shù)據(jù)導入速度不會成為整個項目的瓶頸 2. 新舊存儲數(shù)據(jù)模型差別過大,通過離線數(shù)據(jù)導入也需要大量適配、驗證工作,當時并沒有考慮更加高效的存量數(shù)據(jù)遷移的方案,后期刷全量數(shù)據(jù)約需要 5 天時間,但在存儲遷移的過程中如果能把數(shù)據(jù)遷移的時間壓縮到比較短,如半天能完成存量數(shù)據(jù)的全量遷移,對整個遷移工作是比較有利的,可以進行快速的驗證和試錯。

緩存優(yōu)化

性能優(yōu)化初見成效

在歷史數(shù)據(jù)遷移的過程中,我們也對新存儲層的性能進行了又一次壓測,發(fā)現(xiàn)在數(shù)據(jù)寫入 QPS 到達 3w 時,基本就會把 ByteKV 打掛,雖然此時只有部分機器資源到位,但也開始對性能產(chǎn)生深深的擔憂,因為此時壓測比四月份的壓測更接近真實業(yè)務場景。按照此時的壓測數(shù)據(jù)來看,即使到了開始預估的全量機器,也很可能無法承接所有流量。因此在七月份開啟了緩存的優(yōu)化改造,主要兩點考慮:

  • 期望通過緩存的優(yōu)化能夠提高緩存命中率,減小到達存儲層的流量。
  • 之前新存儲的壓測是在沒有緩存的情況下進行的,需要有額外的緩存資源用來壓測得到更貼近真實的壓測數(shù)據(jù)。而如此大的流量的緩存資源再部署一套是不被接受且浪費的。

主要緩存優(yōu)化的思路是根據(jù)內容云實際業(yè)務場景出發(fā),發(fā)現(xiàn)之前使用緩存的方式存在很大浪費,優(yōu)化思路可能并不能直接復用于其他業(yè)務,這里不詳細展開介紹。但值得注意的是對于類似大型遺留系統(tǒng)由于業(yè)務歷史上的轉變,總會發(fā)現(xiàn)一些系統(tǒng)中不合理的點,經(jīng)過簡單優(yōu)化后可能能得到意想不到的收獲。

簡單說下此階段主要進行了兩點業(yè)務上的優(yōu)化:

  • 在線讀服務的緩存把一篇文章的數(shù)據(jù)分為四份來存儲,在早期來看這種設計的確合理,但由于業(yè)務的發(fā)展,在 18 年之后,四份緩存中的數(shù)據(jù)就存在著大量的重復,造成緩存空間的極大浪費。
  • 在線讀服務之前有兩層服務,兩層緩存,上層的緩存時間 6 分鐘,下層緩存 30 分鐘,上下兩層緩存中的數(shù)據(jù)也基本相同,這使得下層的緩存數(shù)據(jù)比較浪費,因為緩存的數(shù)據(jù)在 30 分鐘內不考慮并發(fā)的情況下只會有 5 次請求。

因此,對在線讀服務的緩存進行了改造,合并了多份緩存的數(shù)據(jù),并且把兩層緩存改為一層,從而釋放出了 Redis 資源供新存儲使用,此次優(yōu)化后緩存命中率得到提升 90%->98%,且節(jié)省出的緩存空間足夠新老兩套存儲同時使用。

經(jīng)過緩存的優(yōu)化,對新存儲加上緩存再進行壓測,此時的壓測數(shù)據(jù)基本可以保證如果預期資源能如約到位,ByteKV 是基本能夠承擔內容云的所有流量的。

讀 diff 消除

每個 diff 的消除都是在解決切換過程中的隱藏炸彈,diff 越仔細,切流時越安心

與很多業(yè)務中臺一樣,內容云的讀服務在讀取數(shù)據(jù)之后進行一些計算打包邏輯,此階段主要對內容云業(yè)務層兩個出口服務的讀接口進行 diff 流程搭建和消除工作。對于讀服務來說進行了一些重構,預期把老的回源服務下掉以保持整體架構的簡潔。服務改造圖如下:

主要改造點:

  • 緩存適配新存儲模型,由于新的存儲是大寬表的模型,無法一次讀出一篇文章所有信息,因此緩存模式需要進行改造適配。
  • 老回源服務的業(yè)務邏輯上移到在線讀服務,業(yè)務和存儲層邊界更加清晰。
  • 計算邏輯適配新存儲。
  • 讀數(shù)據(jù)事件的解析和 diff 打點監(jiān)控。
  • 產(chǎn)出快速查看 diff 和快速修復數(shù)據(jù)的工具,提升消 diff 的效率。

相比于寫 diff 階段,讀 diff 需要消除的 diff 并不算多,更多的 diff 是由于部分需要重構和適配的邏輯與原邏輯沒有對齊導致的,但由于讀接口流量較大,一般無法打印比較詳細的日志,導致對于 diff 的排查工作較難進行,常常需要根據(jù)數(shù)據(jù)和代碼的蛛絲馬跡在腦中進行編譯執(zhí)行來定位具體產(chǎn)生 diff 的原因,這里也是極度需要耐心和細致的過程。

性能優(yōu)化終見曙光

終于找到 ByteKV 的正確打開方式!

讀 diff 消除完成后,理論上已經(jīng)可以進行逐步切流至新存儲,但意外總是不期而遇,最早預估的機器資源由于整體資源緊張并沒有如期到位,導致此時新存儲的資源不能承擔所有流量。因此需要進行進一步的性能優(yōu)化。

在一次小的性能優(yōu)化上偶然發(fā)現(xiàn),寫數(shù)據(jù)時把每次寫存儲的 Key 數(shù)量縮小一半,性能不止能翻一倍?;诒M量減少 Key 的個數(shù)這個思路開始進行代碼的重構和調整(當然又需要全量刷一遍歷史數(shù)據(jù)),主要進行了兩點優(yōu)化:

  • 盡量減少非必要的 Key 寫入,如之前會記錄每個字段的創(chuàng)建、修改時間,但業(yè)務上并沒有實際使用,反而會使 Key 的數(shù)量膨脹為最初的三倍,因此暫時放棄了字段維度時間的記錄。
  • 由于業(yè)務上歷史字段眾多,且由于歷史原因需要全量返回,因此對歷史字段進行了第二版合并,原則是除特殊情況,能合并的都合并。

經(jīng)過上次兩點優(yōu)化,保證了對于大部分請求讀寫一篇文章的數(shù)據(jù),能夠保證讀寫新存儲 4-5 個 Key 即可完成,這使得一切變得美好起來,接口的延遲能夠穩(wěn)定保持在 10ms 以下,錯誤率也不會像之前那樣有突刺了。經(jīng)過優(yōu)化之后再壓測,當前的機器已經(jīng)足以承擔所有流量,甚至還有富裕。

做字段合并是基于內容云的歷史包袱和整體資源不足的無奈之舉,雖然提高了性能,但也會在其他場景引入坑,如非必要請勿作此妥協(xié)。

歷史數(shù)據(jù) diff

對于歷史上的臟數(shù)據(jù)如果無法兼容,嘗試把它改對吧

你永遠無法想象一個歷史遺留系統(tǒng)中的數(shù)據(jù)能有多混亂,歷史數(shù)據(jù)的混亂總在不斷的顛覆對內容云這個系統(tǒng)的認知。如:

  • 一個原則是草稿不會記錄到歷史庫中,但歷史數(shù)據(jù)中竟然發(fā)現(xiàn)好多草稿記錄到了歷史庫中。
  • 不需要記錄版本的字段,卻在版本庫里。
  • 一些不需要記錄版本的業(yè)務,版本號會有幾百甚至上千。
  • 底層的存儲層中竟然有對某一個歷史業(yè)務做的特殊邏輯,導致又花費了一些時間做邏輯的兼容,并重新刷一遍數(shù)據(jù)。

每次發(fā)現(xiàn)這種問題都仿佛是跟前人的一次對話,慢慢可以理解或者想象當時發(fā)生了什么事情,如可能某幾天線上有 bug,造成臟數(shù)據(jù),但并不影響整體使用,逐漸的這些臟數(shù)據(jù)也就留在了遺留系統(tǒng)中。

前期為了保證切換存儲對上游完全透明,即對于這些臟數(shù)據(jù)我們也想辦法盡可能讓他繼續(xù)保持現(xiàn)狀,然后隨著兼容的臟數(shù)據(jù)越來越多,發(fā)現(xiàn)我們新寫的邏輯逐漸不可解釋和維護,最終痛定思痛決定還是按照合理的方式把臟數(shù)據(jù)變成本來該有的樣子(又進行了一遍全量刷數(shù)據(jù)),最終結果發(fā)現(xiàn)把歷史上的臟數(shù)據(jù)改對可能確實是正確的,上游也沒有依賴臟數(shù)據(jù)做邏輯,切換無感知。

切讀

切流量是一個漫長、危險,如履薄冰的過程,需要保證每一步可回滾,可快速恢復

經(jīng)過前面的階段,已經(jīng)基本保證了新存儲讀、寫的功能和性能滿足要求,在 12 月份終于迎來了切量到新存儲。由于此時一些舊存儲的調整導致此時舊存儲的主從延遲問題更加嚴重,導致業(yè)務上反饋較多,因此選擇優(yōu)先把主要讀服務切換到新存儲上。

此步驟主要就是把讀接口流量切換到新的鏈路來承接,本身開發(fā)工作不大,主要是需要觀察切量過程中是否有問題,切量前后的系統(tǒng)流量,穩(wěn)定性等是否滿足需求,同時需要做好線上問題的處理預案。保證任何時候出現(xiàn)問題能夠快速回滾,及時止損。

最終歷時三周時間,把在線讀服務的所有流量切換到新的鏈路上,徹底告別了主從延遲導致的數(shù)據(jù)不一致問題。

切主存儲

在線流量切換完成。需要做好切換過程中的數(shù)據(jù)補償

切主存儲,主要是寫入相關接口,之前還是以舊的存儲作為主存儲,舊存儲成功即返回成功,舊存儲失敗接口返回失敗。需要切換到以新存儲返回為準。需要注意的是需要做好數(shù)據(jù)補償,如切之前,舊存儲成功,新存儲失敗,需要利用舊存儲的數(shù)據(jù)嘗試修復新存儲的數(shù)據(jù),切完之后,新存儲成功,舊存儲失敗需要利用新存儲的數(shù)據(jù)嘗試修復舊存儲的數(shù)據(jù),需要保證切換過程平滑可回滾,不會出現(xiàn)數(shù)據(jù)不一致的 badcase。如下圖,把新存儲切為主依賴,舊存儲切成弱依賴。

最終又歷時兩周,切主存儲完成,在線流量全部切換到新存儲上,整個項目完成。

收益分析

解決多存儲不一致

新的存儲層基于強一致的 ByteKV,不會產(chǎn)生一篇文章部分屬性寫成功,部分寫失敗的問題,切換后消除了不一致問題的反饋。

歷史包袱清理

遷移中附帶解決了業(yè)務中的一些歷史包袱,對歷史不一致臟數(shù)據(jù)嘗試修復,明確業(yè)務層和存儲層的邊界。使整體系統(tǒng)架構更加清晰。

系統(tǒng)可用性提升

存儲層可用性 99.8->大于 99.99%。

更多業(yè)務特性支持

新存儲支持了大 Key 的拆分,解決 MySQL 單列存儲上限問題,滿足部分業(yè)務對單列大容量存儲的需求。

解決容量瓶頸

將 MySQL 替換為計算、存儲分離的 ByteKV,使得存儲容量不再是存儲層的瓶頸。

干掉主從延遲

同樣得益于 MySQL->ByteKV, 切換后無主從延遲導致的緩存臟數(shù)據(jù)問題反饋。

成本降低

  • 新存儲相比舊存儲成本節(jié)省超過 60%。
  • 優(yōu)化緩存使用方式,緩存命中率 90%->98%,節(jié)省 XTRedis 資源。
  • 對 ByteKV 使用方式進行優(yōu)化,完成遷移時只使用了啟動時預估資源的 50%。
  • 遷移中對服務日志進行治理,框架、組件升級,節(jié)省計算資源若干。

總結

本文從存儲層遷移流程的角度詳細闡述了大型系統(tǒng)存儲遷移的過程,分析了其中的難點和過程中的一些坑,總結來說過程中也有一些不足和感悟:

  • 對于寫 diff 應盡量細致和耐心的進行消除,后期再發(fā)現(xiàn)寫數(shù)據(jù)的問題會帶來較多重復的工作,再次強調寫 diff 不管做的多細致都不過分。
  • 歷史數(shù)據(jù)的遷移,如果數(shù)據(jù)量過大應嘗試探索更加高效的遷移手段,遷移邏輯越下沉越高效。
  • 歷史數(shù)據(jù)的 diff 和一些業(yè)務流程上的改造應該盡量前置,后期再進行大的改造需要重新進行刷數(shù)據(jù)、diff 校驗等工作,費時費力。
  • 切流的過程要做好數(shù)據(jù)補償,保證出現(xiàn)任何問題可快速回滾和恢復。
  • 遺留系統(tǒng)中總能發(fā)現(xiàn)一些業(yè)務上使用不合理的點,與其想方設法去提升底層存儲組件的性能(當然也很重要),不如去嘗試進行一些業(yè)務使用方式上的改造,可能能達到意想不到的收獲。

希望能給其他系統(tǒng)做數(shù)據(jù)或存儲層遷移重構帶來一些幫助或參考,能夠更加快速、安全的進行存儲或數(shù)據(jù)的遷移工作。

責任編輯:未麗燕 來源: 字節(jié)跳動技術團隊
相關推薦

2022-08-03 09:29:39

數(shù)據(jù)遷移

2013-06-27 09:52:01

Hyper-V在線存儲

2022-09-09 09:49:46

系統(tǒng)遷移

2012-09-29 10:09:19

網(wǎng)站架構后臺構建架構

2020-03-04 17:37:09

存儲系統(tǒng)硬件層

2023-02-07 15:33:16

云遷移數(shù)據(jù)中心云計算

2012-05-11 16:25:41

IBM ELSIBM大型主機IBM System

2017-07-25 10:57:05

2019-11-27 10:55:36

云遷移云計算云平臺

2022-07-18 10:29:33

數(shù)據(jù)分布式系統(tǒng)

2022-08-10 09:52:16

平臺實踐

2024-09-11 20:05:56

2018-10-24 11:01:53

分布式存儲系統(tǒng)

2021-01-13 09:34:45

項目Gson框架

2023-04-17 08:21:42

2018-10-29 13:07:15

HBase存儲遷移

2016-01-29 10:26:47

云端云遷移

2017-06-22 16:00:07

數(shù)據(jù)庫NoSQL遷移實踐

2022-12-15 15:34:50

數(shù)據(jù)中心云遷移

2019-10-10 09:00:30

云端云遷移云計算
點贊
收藏

51CTO技術棧公眾號