TiDB 在餓了麼歸檔環境的應用

PingCAP發表於2019-03-01

背景

隨著業務增長,公司資料規模不斷膨脹,表變多、變大。一方面佔用的磁碟、CPU 等物理資源疾速上漲,另一方面大表效能下降且變更困難。實際上,很多大表的資料無需保留很久,比如某些業務可能只需近 3 周或近 3 個月的資料。對此類表,可依據業務要求,線上上環境只保留指定天數的資料,其餘超出時間範圍的過期資料可予以刪除。出於某些原因,比如對賬,線上業務查冷資料等,從生產環境刪除的資料不能直接丟棄,需要存檔以備不時之需,所謂歸檔。

方案

目前我們公司主要使用的是 MySQL 資料庫服務,那麼我們使用大磁碟容量的機器搭建幾套主從結構的 MySQL 叢集,配上高可用機制,將生產環境指定庫表、指定時間範圍之外且滿足其他指定條件的資料按照一定的順序定期分批匯入到這些目標叢集,並在每批確認匯入成功後對源生產環境的資料予以刪除,下批再從上次結束的位點開始匯入、刪除,如此迴圈重複,使生產環境表的資料始終維持在指定的時間範圍。至於歸檔目標環境的資料則可以根據公司的要求統一保留指定的時間,比如 1 年。對於歸檔目標環境過期的資料可以進行直接刪除處理。

問題

上述是一個典型的歸檔系統需要完成的基本功能和基本的處理流程,如果資料規模不是很大且增幅穩定而且表結構固定,那麼上述流程可以很好的執行的。但現實的情況是公司資料規模龐大、資料增長迅速,而且由於業務發生變化等原因,後端的表結構可能需要跟著變化。而且,還需要考慮歸檔對生產叢集正常業務的影響、對主從延遲的影響和 DDL 的影響等(這部分內容不在本文討論範圍)。

對於表結構變更有兩種處理方式,

  • “溫和型”:在發現源端表結構發生變更後,相應的在目標端的表上執行同樣的變更,以確保兩端表結構一致資料可以正常寫入;

  • “粗暴型”:一旦發現源端表結構發生了變更,則在目標端輪轉一個新表(舊錶重新命名,然後按源表新的表結構在目標端重建表)以確保源和目標表結構統一。

“粗暴型”的解決方式可以很好的處理結構不一致問題,但如前邊所述,一些線上的業務可能需要查詢歸檔環境的冷資料,比如,使用者想要查詢半年前的訂單資料,對於這類表簡單粗暴的將其重新命名會對業務查詢歸檔資料造成困難;“溫和型”的解決方式不僅可以處理表結構不一致的問題,而且也可以避免輪轉表導致的資料查詢問題,但是,對於 MySQL 來說,當歸檔表變得很大的時候,DDL 通常會非常耗時。

對於資料規模大、資料增長快這一情況,儘管選用了大磁碟容量的機型來儲存歸檔資料,但因表多且資料量大往往在執行幾個月後磁碟空間即被寫滿。這部分資料因為還沒超出指定的有效期,所以還不能從歸檔目標環境直接清理,而 MySQL 又不能方便的進行容量擴充套件,所以只能考慮將現有的歸檔作業遷移至新的歸檔目標叢集進行歸檔,而這一遷移也會對需要訪問歸檔環境資料的應用造成影響。

另外,儘管啟用了高可用機制,因為歸檔環境資料量大,一旦歸檔目標叢集發生了 Master 節點切換,要想重新同步一份資料搭建一個 Slave 節點會非常耗時。

上述問題可簡要概括為三個主要痛點:

  • 快速 Online DDL;

  • 水平擴充套件儲存容量;

  • 故障恢復後的資料自動同步。

探索

彈性伸縮和快速 Online DDL 不是我們在歸檔環境遇到的個案問題,其實也是資料庫業界普遍會遇到的兩個問題,尤其是現如今各類海量資料的大背景下,也因此出現過諸多解決方案。比如,TokuDB 儲存引擎就可以快速安全的處理 DDL,而且是作為 MySQL 外掛的方式提供的,所以對於基於 MySQL 的應用幾乎可以不用進行任何改造就可以在 TokuDB 引擎上執行。而且 TokuDB 引擎有很高的壓縮比,這一點對很多資源敏感型的使用者也很具吸引力。

美中不足的是,TokuDB 只是外掛式的儲存引擎,並不能解決彈性擴容問題。 MySQL Cluster、PXC 等具備彈性擴容能力,但它們只是擴充套件了計算能力,並不能擴充套件儲存能力,且大表的 DDL 依然是個問題。Cassandra,Vertica 這類分散式列式儲存可以對計算和儲存方便的進行彈性伸縮,DDL 也可以快速安全的進行,但這類資料庫是非關係型的,不支援分散式事務(對於歸檔應用不是什麼問題),且對於基於 MySQL 的應用需要進行大量相容性的改造才可能遷移至這些儲存(對於 MySQL 的歸檔意味著我們要進行額外的大量的改造工作)。

基於 Google Spanner 和 F1 之類的資料庫可以橫向擴充套件、可以快速 Online DDL,且是關係型的,支援分散式事務,但卻不支援 MySQL 協議,這對於新的應用不是大的問題,但對於基於 MySQL 的應用的遷移,對於我們目前的 MySQL 歸檔卻是個問題。Hadoop 生態系統的 HBase 存在其他列式儲存同樣的優缺點,且對於我們歸檔應用顯得有點重。

簡單講,我們的選型原則是:能在實現目標的前提下,越簡單越好。

TiDB 分散式資料庫叢集在這一背景下進入我們的視野,它幾乎支援前述提到的各個特性同時高度相容 MySQL,這對於我們的業務是非常友好的。

TiDB

整個 2017 年,TiDB 在資料庫行業發展的如火如荼,我們也對此予以了極大的關注。恰逢我們的歸檔環境存在前述問題,且生產環境也面臨諸多大表擴容和高讀寫表的改造問題亟待解決,因此在歸檔環境率先嚐試使用 TiDB 以解決當前面臨的問題,並對其進行驗證以便將來應用於生產環境變得勢在必行。

1. TiDB 整體架構

TiDB 叢集有三大元件構成:TiDB Server、PD Server、TiKV Server(圖 1)。

圖 1 TiDB 架構

其中各個元件的功能如下:

  • TiDB Server,可以理解為 SQL Layer,負責接收 SQL 請求,處理 SQL 解析、SQL 優化等相關邏輯,並通過 PD 與底層 TiKV 互動來獲取或變更資料;

  • PD Server,可以視作叢集的大腦,負責叢集各類元資訊的儲存以及 TiKV 節點間的資料排程和負載均衡,另外還負責叢集全域性事務 ID 的分配;

  • TiKV Server,叢集的儲存層,資料最終以 Key-Value 的形式儲存於其中,並通過 PD 在各個 KV 節點排程,以保證各節點負載均衡。TiKV Cluster 本身也可作為分散式的 Key-Value 儲存獨立使用。

2. TiDB 核心特性

  • 水平擴充套件計算與儲存

TiDB Server 負責處理 SQL 請求,隨著業務的增長,可以簡單的新增 TiDB Server 節點,提高整體的計算處理能力。TiKV 負責儲存資料,隨著資料量的增長,可以部署更多的 TiKV Server 節點,提高叢集整體的儲存能力。PD 會在 TiKV 節點之間做排程,將部分資料遷移到新加的節點上,這個過程不需要人為的干預。

  • 故障自恢復的高可用

TiDB / TiKV / PD 三個元件都能容忍部分例項失效,不影響整個叢集的可用性。TiDB Server 節點失效,只會影響該節點上的 session,應用連線失敗重試後可通過前端負載均衡中介軟體將請求傳送到其他正常的 TiDB Server 節點。PD 節點失效,若非 Raft leader 節點(PD Cluster 通過 Raft 協議保證自身資料一致性)則無影響,否則會重新選取 leader,期間該無法對外提供服務(約 3 秒鐘)。TiKV 節點失效,會影響該節點上的所有 region,若 region 非 Raft leader(TiKV Cluster 也通過 Raft 協議保證節點間的資料一致性),則服務不受影響,否則服務會中斷,待重新選舉 leader 後恢復。如果 PD 確認了失效的 TiKV 節點已經不能恢復,則會自動將該節點的資料排程至其他正常的 TiKV 節點。

3. TiDB 其他特性及原理

  • 高度相容 MySQL 語法和協議;

  • 分散式事務;

  • 跨資料中心資料強一致性保證;

  • 海量資料高併發實時寫入與實時查詢。

至於 TiDB 的內部原理,比如,資料在 TiKV 層是如何組織和管理的,又是如何保證資料一致性的;SQL 層是如何實現的,又是如何將關係型表結構、關係型資料、索引、SQL 對映為 K-V 模型的;PD 是如何保證自身後設資料一致性的,又是如何協調 TiDB、排程 TiKV 實現負載均衡的超出了本文的範圍。針對原理部分,TiDB 有三篇系列文章將這些問題描述的很清楚,詳情可參考(以下文字可跳轉至原文):

可彈性擴容,故障自恢復且可自動遷移資料,高度相容 MySQL 協議,快速 Online DDL,這是我們計劃選擇 TiDB 作為歸檔目標叢集所考慮的主要四點。

TiDB 在餓了麼歸檔環境的實踐

1. 初試

整個實踐過程並非一帆風順,部署和測試期間遇到過不少問題。經歷了從單機 12 例項部署 TiKV,到單機 4 例項部署,再到目前的單機 3 例項部署。也經歷了 TiDB 版本由 RC 升級 PreGA、由 PreGA 升級 GA1.0.0、再由 1.0.0 升級到目前的 1.0.4。還有期間各種壓縮、快取、排程等引數的組合測試與調優和各類 bug、疑難問題的排查與解決。

起初,考慮到歸檔環境資料量龐大,且對於分散式的 TiDB 來說本身會儲存多份資料副本無需對磁碟進行 RAID,而且機器記憶體和 CPU 充足,因此將每臺物理機的 SATA 磁碟拆成了獨立的 12 塊,每塊盤上部署一個例項,以增加叢集總的可用空間。這樣一共 3 臺物理機共部署了 36 個 TiKV 例項來進行測試,TiDB 和 PD 各 3 個例項共享另外 3 臺物理機。在使用 sysbench 壓測過程中執行緒數開到 64 的時候(64 個表,單表 3000W 資料),持續的高併發寫入導致資料在 TiKV 例項間分佈極不均衡,寫入的資料集中在其中 3 個 TiKV 例項,其他例項上的資料非常少,且沒有看到預期的明顯的資料從 region 較多的節點向 region 較少的節點排程的跡象。後臺大量的 server busy 報錯,監控頁面大量的底層 RocksDB stall 資訊。

後期分析發現是 SATA 盤的效能太弱,而 TiDB 的各類預設引數都只針對 SSD/NVMe 盤做了優化(這也是官方的安裝程式檢測磁碟效能的原因),且當時的 TiDB 版本比較老,還沒有熱點排程功能,持續的併發寫入導致磁碟 IO 飆升,region 數量快速增長且熱點集中,大量的 region 排程又引起大量的 snapshot 操作,從而又加劇了磁碟 IO 的消耗,最終導致了惡性迴圈,region 在最初寫入的節點不斷堆積,無法排程出去。後期,將 12 塊盤以 RAID10 方式打成了 1 塊盤以提升效能,單臺物理機的 TiKV 例項數量也由 12 降至了 3,增加了 block-cache-size 的大小,同時,考慮到機器 CPU 效能較強悍,調高了 TiKV 預設的 compression-level 以儘可能的減少需要排程的資料量,也將版本升級後遇到的問題得以解決。後續對 TiDB 的擴縮容,online DDL,高可用進行了測試,也都符合預期。

儘管 TiDB 是相容 MySQL 語法和協議的,但在測試過程中還是碰到一些小的問題。比如,暫不支援臨時表,部分 SQL Hints 語法暫不支援,SELECT MIN()\MAX() 查詢有索引的列也會比較慢等。對此,我們對歸檔應用進行了相容性的改造。比如,針對 SELECT MIN()\MAX() 查詢使用等價的 ORDER BY + LIMIT 1 的方式變通等(新版本已經直接支援了)。任何產品從最初開發到最終的成熟應用,迭代過程中必然會出現一些 bug,在使用 TiDB 的過程中也遇到過一些部署、授權、監控、PD-CTL 檢視 store 狀態資訊等方面的問題。不過在反饋給 TiDB 官方團隊後,都得到了快速的響應和解決。

在 TiDB 叢集測試基本滿足需求後,我們並沒有直接將現有的所有歸檔作業遷移至 TiDB。畢竟模擬壓測和真實的應用環境還有比較大的差距,我們計劃讓 TiDB 承載儘可能接近真實的歸檔工作來進一步考驗其表現。TiDB 官方提供了一個很好的工具—— Syncer 來做這個事情。

Syncer 可以模擬成 MySQL Slave 節點從上游讀取和解析 Binlog 並在匹配過濾規則後應用於下游的 TiDB(圖 2)。Syncer 支援斷點續傳功能,可以同時起多個 Syncer 例項分別使用不同的過濾規則從一個資料來源向到多個目標進行資料同步,也可以從多個資料來源向同一個目標進行資料同步。我們選擇了當前負載比較高的兩套 MySQL 歸檔叢集作為資料來源,使用 Syncer 將每天夜裡歸檔期間的增量資料(百 GB 數量級)實時同步至 TiDB 叢集。同步持續執行了一週左右,未發現有明顯異常。

圖 2 Syncer 架構

2. 應用

即使利用 Syncer 同步資料的方案在 TiDB 叢集模擬了歸檔期間的負載,但畢竟順序應用 Binlog 時事務是單執行緒執行的,同直接在 TiDB 進行歸檔時的多執行緒併發執行的負載也還是有區別。所以,我們選擇分批遷移歸檔作業至 TiDB 叢集,每次一小批,執行一段時間無異常後遷移下一小批。

截至目前,已有 45% 左右的歸檔作業遷移至了 TiDB 叢集。至此 TiDB 叢集一直平穩執行。當前的歸檔方案(圖 3)。

圖 3 歸檔架構

TiDB 叢集的部署情況和叢集監控告警架構(如圖 4、圖 5)。

圖 4 TiDB 叢集部署情況

對於歸檔表我們進行了分類處理,對於需要業務訪問的表,在源和目標表結構不一致時會同步變更目標環境的表結構,對不需要業務訪問的表,在遇到表結構不一致時或者每月月初會自動輪轉一個新表,以便於後續的歸檔順利進行和後期的過期資料處理。

由於 DDL 在 TiDB 是序列執行的,且大部分表無需業務訪問,所以在月初會有近五萬張表的 rename、create 和 drop 操作在 TiDB 排隊等待執行,造成較多的歸檔超時失敗。對於這一部分的處理,我們計劃將輪轉表的邏輯從歸檔流程中解耦,在非歸檔時段提前將錶慢慢輪轉掉,到歸檔開始進行時便無需等待 DDL。

圖 5 TiDB 叢集監控告警方案

3. 成果

截至目前整個歸檔平臺已部署歸檔作業千餘個,涉及數百套源叢集數百個庫數百張表。其中 TiDB 上部署的歸檔作業佔總歸檔作業量的 45%,涉及近百套源叢集幾十個庫上百張表。整個歸檔平臺日均資料增量數億行,大小數百 GB,其中 MySQL 佔 75% 左右,TiDB 佔 25% 左右。

目前 TiDB 叢集的總容量幾十 TB,已歸檔的資料量佔 TiDB 叢集總空間約 20%。後期,在現有 TiDB 叢集容量不足時只需新增 TiKV 節點即可實現容量的擴充套件而無需遷移之上的歸檔作業,不影響業務的訪問。對於源端發生了結構變更的表,也可以方便快速的在歸檔目標同步這個變更。如果叢集中節點出現了故障也不會影響整個叢集的訪問,且在替換掉故障節點後資料會自動同步至新節點,無需人工干預。

當前 TiDB 尚存在的一些問題

  • TiDB 中 DDL 序列執行,當某種原因在同一時刻有大量 DDL 請求時,會排隊等待,待所有的 DDL 都執行完成可能需要很長時間。希望能增加 DDL 併發執行的特性。

  • 當前需要手動對 TiDB 上的大表進行 Analyze 操作以收集準確的索引統計資訊,以便於查詢的高效執行。希望可支援自動取樣分析來保證執行計劃的準確性。

  • TiDB Binlog 依賴於 Kafka 叢集,感覺架構有點重。

我們也就以上三點諮詢了 TiDB 官方,下面是官方回覆:

  • 並行 DDL 正在做,預計會在 2.1 版本初步提供,3.0 版本完善。

  • 目前釋出 2.0 版本已經提供了統計資訊的動態更新以及自動全量 analyze,不過還沒有預設開啟,測試穩定後,會預設開啟。

  • 為保證 Binlog 高可用,依賴於分散式訊息佇列在所難免,Kafka 是其中最好的解決方案。

關於使用 TiDB 的一些建議

  • 多例項部署 TiKV 時, capacity、block-cache-size 等引數一定要設定好,否則可能導致磁碟空間溢位和記憶體溢位。每個 region 也會佔用很少一部分的記憶體,隨著叢集 region 數量上升,TiKV 使用的記憶體量會緩慢上漲,需要留意。

  • 在磁碟使用量達到了設定的 capacity 的 80% 左右時,叢集會控制不往這些節點排程資料,但正常的資料寫入依然會進行。另外,擴容需要額外的臨時空間,所以需要提前做好擴容準備。建議在容量 60% 的時候就開始準備擴容工作。

  • 多例項部署 TiKV 時需要設定好 label,以便於將資料儘可能的寫到不同機房、機架和主機,避免幾個副本落在同一臺機器,保證各節點負載相對均衡。

  • TiDB 對單個事務大小有限制,主要原因是對大規模的資料做兩階段提交和多副本複製壓力太大。在使用 mydumper 工具匯出並使用 myloader 工具匯入資料時常會碰到這個問題,可以通過加 -F 引數限制 mydumper 匯出的單個 insert 的大小來避免。

計劃

TiDB 在歸檔環境的成功應用是我們的一個良好開始,也為我們日後嘗試在生產環境使用積累了不少經驗,接下類我們計劃再搭建 1~2 套 TiDB 叢集,將剩餘的未遷移至 TiDB 的歸檔作業全部遷移至 TiDB。

另外,目前生產環境也存在較多的大表治理問題和海量資料的推送問題。傳統的 Sharding 方案對一些不好確定 sharding key 的大表無能為力, MySQL 主從方案對於海量資料的推送也力不從心。因此我們需要找到解決方案來應對這些挑戰。

致謝

在 TiDB 部署與測試過程中,得到了 PingCAP 公司同事的大力支援。對他們的及時響應和耐心解答深表敬佩和謝意。

TiDB 背後有這麼一支強悍的精於技術和服務的隊伍,想必一定會走的很長很遠!

作者:張延召,餓了麼高階資料庫工程師

相關文章