TiDB Ecosystem Tools 原理解讀系列(二)TiDB-Lightning Toolset 介紹

做者:Kenny Chanmysql

簡介

TiDB-Lightning Toolset 是一套快速全量導入 SQL dump 文件到 TiDB 集羣的工具集,自 2.1.0 版本起隨 TiDB 發佈,速度可達到傳統執行 SQL 導入方式的至少 3 倍、大約每小時 100 GB,適合在上線前用做遷移現有的大型數據庫到全新的 TiDB 集羣。git

設計

TiDB 從 2017 年開始提供全量導入工具 Loader,它以多線程操做、錯誤重試、斷點續傳以及修改一些 TiDB 專屬配置來提高數據導入速度。github

然而,當咱們全新初始化一個 TiDB 集羣時,Loader 這種逐條 INSERT 指令在線上執行的方式從根本上是沒法盡用性能的。緣由在於 SQL 層的操做有太強的保證了。在整個導入過程當中,TiDB 須要:sql

  • 保證 ACID 特性,須要執行完整的事務流程。數據庫

  • 保證各個 TiKV 服務器數據量平衡及有足夠的副本,在數據增加的時候須要不斷的分裂、調度 Regions。服務器

這些動做確保 TiDB 整段導入的期間是穩定的,但在導入完畢前咱們根本不會對外提供服務,這些保證就變成畫蛇添足了。此外,多線程的線上導入也表明資料是亂序插入的,新的數據範圍會與舊的重疊。TiKV 要求儲存的數據是有序的,大量的亂序寫入會令 TiKV 要不斷地移動原有的數據(這稱爲 Compaction),這也會拖慢寫入過程。網絡

TiKV 是使用 RocksDB 以 KV 對的形式儲存數據,這些數據會壓縮成一個個 SST 格式文件。TiDB-Lightning Toolset使用新的思路,繞過SQL層,在線下將整個 SQL dump 轉化爲 KV 對、生成排好序的 SST 文件,而後直接用 Ingestion 推送到 RocksDB 裏面。這樣批量處理的方法略過 ACID 和線上排序等耗時步驟,讓咱們提高最終的速度。多線程

架構

TiDB-Lightning Toolset 包含兩個組件:tidb-lightning 和 tikv-importer。Lightning 負責解析 SQL 成爲 KV 對,而 Importer 負責將 KV 對排序與調度、上傳到 TiKV 服務器。架構

爲何要把一個流程拆分紅兩個程式呢?併發

  1. Importer 與 TiKV 密不可分、Lightning 與 TiDB 密不可分,Toolset 的二者皆引用後者爲庫,而這樣 Lightning 與 Importer 之間就出現語言衝突:TiKV 是使用 Rust 而 TiDB 是使用 Go 的。把它們拆分爲獨立的程式更方便開發,而雙方都須要的 KV 對能夠透過 gRPC 傳遞。

  2. 分開 Importer 和 Lightning 也使橫向擴展的方式更爲靈活,例如能夠運行多個 Lightning,傳送給同一個 Importer。

如下咱們會詳細分析每一個組件的操做原理。

Lightning

Lightning 現時只支持經 mydumper 導出的 SQL 備份。mydumper 將每一個表的內容分別儲存到不一樣的文件,與 mysqldump 不一樣。這樣不用解析整個數據庫就能平行處理每一個表。

首先,Lightning 會掃描 SQL 備份,區分出結構文件(包含 CREATE TABLE 語句)和數據文件(包含 INSERT 語句)。結構文件的內容會直接發送到 TiDB,用以創建數據庫構型。

而後 Lightning 就會併發處理每一張表的數據。這裏咱們只集中看一張表的流程。每一個數據文件的內容都是規律的 INSERT 語句,像是:

INSERT INTO `tbl` VALUES (1, 2, 3), (4, 5, 6), (7, 8, 9);  
INSERT INTO `tbl` VALUES (10, 11, 12), (13, 14, 15), (16, 17, 18);
INSERT INTO `tbl` VALUES (19, 20, 21), (22, 23, 24), (25, 26, 27);

Lightning 會做初步分析,找出每行在文件的位置並分配一個行號,使得沒有主鍵的表能夠惟一的區分每一行。此外亦同時將文件分割爲大小差很少的區塊(默認 256 MiB)。這些區塊也會併發處理,讓數據量大的表也能快速導入。如下的例子把文件以 20 字節爲限分割成 5 塊:

Lightning 會直接使用 TiDB 實例來把 SQL 轉換爲 KV 對,稱爲「KV 編碼器」。與外部的 TiDB 集羣不一樣,KV 編碼器是寄存在 Lightning 進程內的,並且使用內存存儲,因此每執行完一個 INSERT 以後,Lightning 能夠直接讀取內存獲取轉換後的 KV 對(這些 KV 對包含數據及索引)。

獲得 KV 對以後即可以發送到 Importer。

Importer

因異步操做的緣故,Importer 獲得的原始 KV 對註定是無序的。因此,Importer 要作的第一件事就是要排序。這須要給每一個表劃定準備排序的儲存空間,咱們稱之爲 engine file。

對大數據排序是個解決了不少遍的問題,咱們在此使用現有的答案:直接使用 RocksDB。一個 engine file 就相等於本地的 RocksDB,並設置爲優化大量寫入操做。而「排序」就相等於將 KV 對全寫入到 engine file 裏,RocksDB 就會幫咱們合併、排序,並獲得 SST 格式的文件。

這個 SST 文件包含整個表的數據和索引,比起 TiKV 的儲存(Osc和諧詞,請忽略)單位 Regions 實在太大了。因此接下來就是要切分紅合適的大小(默認爲 96 MiB)。Importer 會根據要導入的數據範圍預先把 Region 分裂好,而後讓 PD 把這些分裂出來的 Region 分散調度到不一樣的 TiKV 實例上。

最後,Importer 將 SST 上傳到對應 Region 的每一個副本上。而後經過 Leader 發起 Ingest 命令,把這個 SST 文件導入到 Raft group 裏,完成一個 Region 的導入過程。

咱們傳輸大量數據時,須要自動檢查數據完整,避免忽略掉錯誤。Lightning 會在整個表的 Region 所有導入後,對比傳送到 Importer 以前這個表的 Checksum,以及在 TiKV 集羣裏面時的 Checksum。若是二者同樣,咱們就有信心說這個表的數據沒有問題。

一個表的 Checksum 是透過計算 KV 對的哈希值(Hash)產生的。由於 KV 對分佈在不一樣的 TiKV 實例上,這個 Checksum 函數應該具有結合性;另外,Lightning 傳送 KV 對以前它們是無序的,因此 Checksum 也不該該考慮順序,即服從交換律。也就是說 Checksum 不是簡單的把整個 SST 文件計算 SHA-256 這樣就了事。

咱們的解決辦法是這樣的:先計算每一個 KV 對的 CRC64,而後用 XOR 結合在一塊兒,得出一個 64 位元的校驗數字。爲減低 Checksum 值衝突的機率,咱們目時會計算 KV 對的數量和大小。若速度容許,未來會加入更先進的 Checksum 方式。

總結和下一步計劃

從這篇文章你們能夠看到,Lightning 由於跳過了一些複雜、耗時的步驟使得整個導入進程更快,適合大數據量的初次導入,接下來咱們還會作進一步的改進。

提高導入速度

現時 Lightning 會原封不動把整條 SQL 命令拋給 KV 編碼器。因此即便咱們省去執行分佈式 SQL 的開銷,但仍須要進行解析、規劃及優化語句這些沒必要要或未被專門化的步驟。Lightning 能夠調用更底層的 TiDB API,縮短 SQL 轉 KV 的行程。

並行導入

另外一方面,儘管咱們能夠不斷的優化程序代碼,單機的性能老是有限的。要突破這個界限就須要橫向擴展:增長機器來同時導入。如前面所述,只要每套 TiDB-Lightning Toolset 操做不一樣的表,它們就能平行導進同一個集羣。但是,如今的版本只支持讀取本機文件系統上的 SQL dump,設置成多機版就顯得比較麻煩了(要安裝一個共享的網絡盤,而且手動分配哪臺機讀取哪張表)。咱們計劃讓 Lightning 能從網路獲取 SQL dump(例如經過 S3 API),並提供一個工具自動分割數據庫,下降設置成本。

在線導入

TiDB-Lightning 在導入時會把集羣切換到一個專供 Lightning 寫入的模式。目前來講 Lightning 主要用於在進入生產環境以前導入全量數據,因此在此期間暫停對外提供服務還能夠接受。但咱們但願支持更多的應用場景,例如回覆備份、儲存 OLAP 的大規模計算結果等等,這些都須要維持集羣在線上。因此接下來的一大方向是考慮怎樣下降 Lightning 對集羣的影響。

相關文章
相關標籤/搜索