劉寅:TiDB 工具鏈和生態

本文為今年年初 PingCAP 商業產品團隊負責人劉寅在 TiDB DevCon2018 上分享的 《 TiDB 工具鏈和生態》實錄內容,文內詳細介紹了 TiDB 的周邊工具以及生態系統。Enjoy~

大家下午好,我叫劉寅。在 PingCAP 主要負責 TiDB 商業工具產品開發,也在做公司 SRE 方面的事情。今天下午我分享的主題是介紹下 TiDB 的周邊工具以及生態系統。

今天要講的內容主要包含這幾方面,首先是關於 TiDB 的部署,這是很多使用 TiDB 的用戶首先關心的事情。接下來會介紹 TiDB 的數據導入工具和數據遷移同步工具,以及管理配置,數據可視化相關的工具。

TiDB 的架構可能大家都比較清楚了。TiDB 是一個由若干模塊組成的分散式系統。這些模塊相互依賴協調工作組成一個集群,整體構成了 TiDB 資料庫。這樣一個架構,對於用戶進行部署和運維,其複雜程度相對單機資料庫比如 MySQL 來說不那麼容易的事情。那讓我們來看看如何快速部署一套 TiDB 集群實例。最近我們公開了一個項目( github.com/pingcap/tidb ),這令我們在一個本地的開發和測試環境上跑一套 TiDB 變得非常簡單。只需要用一個命令 docker compose up 就能快速啟動起來。docker-compose 是 Docker 生態中的一個非常便利的工具,它可以在本機方便的把 TiDB 的各個組件,包括它的監控,可視化工具,全部整合在一個 yaml 文件來描述,非常的方便。不僅可以通過我們官方 docker image 鏡像啟動,也可以支持從本地的 binary 啟動。比如當我本機編譯了一個特殊版本的 binary,我就可以直接構建本地鏡像來啟動,甚至還可以支持現場編譯源碼來啟動。所以這對於我們自己開發和測試也是非常方便的。另外我們也做了一個很簡化的配置文件,比如我不希望默認跑 3 個 TiKV,我想啟 5 個或者更多,簡單的改下配置就可以搞定。

對於生產環境的部署和運維,往往面對的是一個成規模的集群,docker-compose 的部署方式就不夠了。我們建議採用提供的 Ansible 部署方式。用戶首先在一個 Inventory 文件中描述和編排所需的 TiDB 集群拓撲,然後執行我們提供的 ansible-playbook 腳本,就可以快速部署和運維一個生產環境下的 TiDB 集群。我們現在很多的線上用戶,也是用了這樣的部署方式。

TiDB Ansible 不僅實現在裸機上部署集群,同時也支持 Cloud 的部署方式。比如說用 Ansible 提供的組件,我們可以基於 AWS / Azure / GCP 上一鍵創建 TiDB 的集群,而將來也會支持國內的公有雲平台。其次可以根據用戶需求,定製集群的拓撲。這個比較細,也會包含 TiDB 的一些周邊工具的部署,比如說 TiDB Binlog 組件。第三,它提供一個配置管理的功能,包括 TiDB、TiKV 很多的參數配置。我們也集成進去,可以在一個地方統一管理整個集群的配置。除此之外,我們對運維操作的執行腳本做了一系列的優化。這樣對於在部署一個規模龐大的集群會變得及其方便。另外這裡順便還要提一下,我們在 Ansible 部署過程中,我們會對硬體和系統環境做一個嚴格的檢查。可能有些用戶出於測試的目的,使用較低速的機械硬碟,而達不到跑 TiDB 的最低要求。所以這裡,我們會有限定要求,會在安裝過程中交互提示出來。

TiDB 作為一個可以彈性水平擴展的分散式資料庫,天生為雲而設計,從初期我們就和容器走的非常近。容器的優勢,相信大家都非常了解。首先,它提供了一致化的環境,用戶不需要去適應各種不同的系統環境,而分別構建運行時 Binary。另外容器的啟動運行非常方便,可以很快速的在開發環境運行或者生產環境部署。另外容器提供了資源隔離的特性,通過 Namespace 和 CGroups 這些現代操作系統提供的能力,來實現容器內部和外部的資源隔離和限制。

說到容器就不得不提容器編排,TiDB 在與 K8s 的整合方面我們做了非常多的事情。比如在 K8s 之上實現對 TiDB 集群的自動化管理,快速部署、擴縮容、以及故障的自動癒合。同時更好的支持雲平台下的多租戶管理,通過限制單個租戶的資源的使用,利用容器完成隔離。來保證租戶之間不會相互影響。不至於說一個用戶執行高負載的查詢或者寫入,對同一台宿主機上的其他用戶實例造成影響。然而 TiDB 存儲本身是有狀態的,在 K8s 上部署的時候,如何管理好有狀態的服務,並且保證存儲的 iops 和延遲方面苛刻的要求,同時還要保證服務的高可用性就成為一個難題。

如果採用 K8s 提供的 native 存儲解決方案,外掛 PV,也就是掛網路存儲。但是這樣對於資料庫系統來說,尤其是大量的隨機讀和順序寫的場景下,網路盤的性能是達不到要求的。所以說從最開始我們設計 TiDB 上雲解決方案,其實主要就是探索 K8s 的本地 PV 解決方案。當然現在 K8s 1.9 已經開始對 Local PV 有一定支持,而我們在 1.7 的時候就實現了一個 Local Storage Manager。我們現在做的一些工作,也逐漸在和社區 K8s 主版本進行整合。另外 TiDB 本身是一個複雜集群,除了存儲還有網路,以及周邊工具的管理都需要考慮。為了實現將專業領域的運維管理變的更加自動化,我們造了 TiDB Operator。Operator 這個 pattern 其實是最初借鑒 CoreOS 的 Etcd Operator。TiDB Operator 就是降低 TiDB 部署和運維的複雜度,實現自動化的擴縮容和故障轉移。同時 Operator 在 K8s 上同時管理多套 TiDB 集群,像在騰訊雲和 UCloud 兩個公有雲上,就是用這種方式來實現多租戶統一化管理。我們實現的 Local PV 管理機制,實質上是對集群中所有本地磁碟的統一管理,並賦予他們生命周期,從而作為 K8s 中的一類資源參與調度。同時新版本 K8s 的趨勢上,在往雲上的操作系統方向上發展,自身的資源以及 API 變的更加開放。我們不需要去改動 K8s 本身的代碼,而是去做更好的擴展,來實現滿足自己的調度功能。比如說我們利用 K8s 親和性的特點,讓同種類型的服務運行在同一台物理機上,更充分的利用硬體資源。再比如說 PD 和 TiKV 這兩種服務,你不能在一起混部使用同一塊 SSD,否則 IO 會相互影響。所以我們利用反親和的特性,讓 PD 和 TiKV 調度的時候盡量分開。另外再舉一個調度的例子,TiDB 集群本身是支持區分節點的地域屬性的,PD 根據地域屬性來實現數據層面的調度,並且盡量保證同一份數據的多個副本儘可能按地域分散開。那麼 K8s 部署 TiDB 節點的時候,也需要考慮地域特徵來進行調度。比如按照跨 Region、跨可用區將一個集群的節點分散部署,並且把地域的信息傳遞給 TiKV 和 PD,使數據副本盡量分散。而這個知識本身 K8s 是不具備的,我們需要擴展 K8s 的調度器把經驗和原則傳遞進去。

Operator 包含一些 TiDB 擴展的 Controller 和 Scheduler,但還不夠,我們需要在上面包裝一層,以暴露出來統一的運維和管理介面,這就是 Cloud Manager。Cloud Manager 對外暴露標準化的介面,用於和雲平台的前端控制台對接,這樣就通過前台可以完成 K8s 以及 TiDB 集群的相關資源的綜合管理。

DBaaS 結構圖可以看到 Cloud TiDB 的分層架構。最下層是容器雲,中間一層是 K8s 自身的服務管理和 API Server。我們在此基礎上進行擴展,實現各種 Controller 和調度器,自己本地存儲管理 Volume Manager,最終通過 Cloud Manager 提供的 RESTful API 進行暴露。可以很容易接一個前端的 Dashboard,或者直接使用 CLI 命令行工具,完成 TiDB 集群實例的統一化管理。

這個圖就是前面講的一些細節。這裡面可以看到,左半邊是 Kube 本身的組件,右側是我們的擴展出來組件,另外,我們也自己定義了一些 TiDB 的資源類型放在 CDR 裡面。比如說 TiDB Cluster,在這個資源對象上可以描述要啟動多少個 TiKV,多少個 TiDB。另外還有 TiDB Set / TiKV Set / PD Set 等一系列對象,來分別描述某個服務的配置。

這是在騰訊雲上面的一個截圖,

這是 UCloud 的截圖,

現在這兩個產品都在公測,有興趣的同學可以關注一下。

此外,我們提供了 Operator Chart 的安裝方式,使用 Helm 工具可以一鍵通過 Operator 拉起來一套 TiDB 實例。

這種方式在 K8s 上就更像是一個 RPM 包的方式部署服務,並且管理服務之間依賴。只需要一行命令,就可以獲得到官方的 Cloud TiDB 的核心組件。如果你有一個 K8s 集群,或者你在使用一個公有雲提供的 K8s 集群,用上面的命令,就可以快速運行 TiDB Operator 和 TiDB 集群。

這是一個配置的例子,打開 charts 壓縮包可以找到對應的配置 yaml 文件。

我們對每一行的配置做了詳細的注釋。比如可以設定一些參數:像副本數、CPU 內存使用限制、TiDB 起多少個、TiKV 起多少個,等等。

部署工具就先介紹這麼多。下一部分,我們開始介紹一下 TiDB 周邊的工具,其實這裡面有一些大家已經接觸和使用過了。

首先是 Syncer,這個小工具在很多生產環境上已經用起來了。它是一個 MySQL 到 TiDB 間的實時同步工具。原理很簡單,就是把自己偽裝成一個 MySQL 的 Slave 庫,從上游 MySQL 裡面把 binlog 實時 dump 出來,並且還原成 SQL 到下游(TiDB)回放。

這裡我們支持簡單的規則過濾,也支持分庫分表的合併。我們也可以同時跑多個 Syncer 把多個上游 MySQL,按庫同步到一個大的 TiDB 集群。Syncer 的主要一些特性,首先是要支持按 GTID 同步。GTID 是什麼?它是 MySQL 自身的 replication 機制提供的一種特性。MySQL 主從同步最早是以 binlog pos(文件名+offset)來描述同步位置,但這個設計有明顯的缺陷,比如說這樣一個場景,最初是 1 個 Master 帶 2 個 Slaves,當 Master 掛了這時需要把一個 Slave 升級為 Master,另一個 Slave 從新 Master 繼續同步。但這樣就要保證,新的 Master 和舊 Master 的 binlog pos 能接續上,但是 MySQL 不同實例的 binlog 記錄方式是不同的,因此必須有一個全局唯一 ID 來和 binlog 對應上,這就是 GTID。在 MySQL 5.6 之後 GTID 支持的就比較好了,生產環境大多是開啟了這種方式。Syncer 除了支持按 pos 同步,也支持 GTID。Syncer 從公有雲的 RDS 同步支持的都比較好,比如像阿里雲、騰訊雲我們測的也比較多,因為雲平台後端機器故障或者維護,主從切換比較頻繁,而且 Virtual IP 還保持不變對用戶無感知,所以假如 Syncer 不能很好支持 GTID 的話那切一次主從數據就會不一致了。第二是分庫分表合併。不管上游庫是按庫拆,按表拆,甚至混合拆分,Syncer 都能很好支持,通過配置文件描述出來。另外還有同步性能的問題,因為 binlog 是一個單向數據流,我們同步的時候如果是單線程來做雖然比較簡單,但性能可能很差。使用多線程,就必須區分對同一行數據操作的因果順序,沒有關聯關係的行可以並行執行,有關聯的行只能順序執行。對於 MySQL 每一個 binlog event 都是一個事務,他裡面會包含對不同表,不同行的多次操作。所以 Syncer 會對事務進行拆分,然後並行執行。這樣的代價是 Syncer 不保證按上游的事務原子性來同步,但最終一致性沒有問題。Syncer 也支持一些簡單的過濾規則,可以選擇指定庫或者表同步,也可以做排除。另外也支持一些簡單的表名映射變換。

在一個公司初期,可能業務鋪的比較快,每塊業務用一個 MySQL 庫,不同的業務之間數據是隔離的。後來業務複雜了,可能 MySQL 要掛從庫了。從庫專門用於一些數據分析的場景,而不能影響主庫支撐線上的讀寫。隨著進一步的發展,數據分析可能要跨業務線,那麼跨庫進行統計查詢,比如 Join 和 Sub Query 這樣的操作基本上很難。這個場景下我們可以把一個 TiDB 集群作為所有線上 MySQL 的 Slave,而使用 Syncer 完成同步。數據分析團隊可以在 TiDB 中完成複雜的關聯查詢和分析,這跟使用 MySQL 沒有什麼區別。而且 Syncer 同步的實時性很高,使後端的分析可以做到非常的實時。

接下來我們介紹一下 TiDB Binlog。TiDB Binlog 本質上不同於 MySQL,這個要聲明一下,我們的 binlog 跟 MySQL 的 binlog 格式不同,TiDB 採用一種自描述的 protobuf 格式的 binlog。而每個 TiDB Server,都會寫自己的 binlog,一個事務就是一個 binlog event。然後通過一個叫作 Pump 的小程序,匯總寫入到 Kafka 集群。Pump 直接寫本地就好了,為什麼還要用 Kafka?這是考慮到 log 落本地盤會有單點故障的風險。所以採用 Kafka 或者一個分散式文件系統來解決這個問題。在下游有一個叫 Drainer 的組件來消費 Kafka 的數據。Drainer 的職責是將 binlog 按照事務的順序還原成 SQL,同步到下游資料庫,比如 MySQL,也可能是另外一個 TiDB 集群,還可以寫到文件流實現增量數據備份。

其實 Drainer 做的事情是有一些難度的,因為 TiDB 不像 MySQL,他是一個分散式系統,大家可以思考一下。首先,怎麼保證事務的完整性,什麼意思呢,因為 TiDB 的事務大家都知道是兩階段事務。那麼有可能事務提交成功,但是 binlog 沒有寫成功;也有可能事務沒有寫成功但是 binlog 發出去了,這兩種情況都可能導致不一致。第二點,如何來還原分散式事務之間的因果順序。TiDB 事務是提交到 TiKV 上來執行,每個事務又是兩階段,事務的順序號是由 PD 產生,在同一個 TiDB 節點上可能會並發執行多個事務,所以產生的 binlog 的事務 seq 不能保證單調遞增,那如何還原順序並實時輸出。第三點,網路本身可能也是不可靠的,你可能寫到 TiDB 是前一個事務在前,一個在後。而在網路傳輸的過程中,順序可能變化。在多機都在產生 binlog 的情況下,最終落到 Drainer 的順序是錯亂的,那麼如何進行順序還原。這個似乎跟 TCP 有點像,但又不太一樣。在 TiDB 裡面事務的全局順序編號並不是連續遞增,所以說當 Drainer 收到了一個 binlog 的時候,永遠不知道下一個 binlog 的事務編號是多少。至於實現,我們設計了一個比較複雜的動態窗口演算法。時間關係我就不展開講,大家有興趣可以思考一下。

在場景方面,我們用 TiDB Binlog 可以做很多事兒。比如在 TiDB 集群上再掛一個從集群。也可以同步到 MySQL 做從庫。像一些客戶在線上初期開始使用 TiDB 可能會比較謹慎,開始把 TiDB 通過 Syncer 掛到 MySQL 的後面做一個從庫,跑一段時間驗證覺得沒有問題,就可以把它調換一下。TiDB 成為主庫,用 binlog 去反向同步到 MySQL。再跑一段時間覺得 OK 了很安全,就可以把 MySQL 從庫摘下來,這樣就完成了一個灰度上線的過程。此外我們還可以用 binlog 去同步其他異構資料庫,或者一些數據倉庫、或者分散式存儲產品。包括我們也在研發自己的 OLAP 的存儲引擎。將來都是通過 binlog 來完成數據實時同步。只需要給 Drainer 寫不同的 Adapter 插件即可。

TiDB Binlog 還可以用於數據增量備份,可以找到最近的一個全量備份點,然後回放這段時間的 Binlog,就可以還原到任意時間點的數據狀態。另外還有一些場景,比如說有的公司業務希望在 binlog 基礎上實現事件訂閱。我們可以通過監聽 binlog,當監測到某個業務數據發生變化的時候往 Kafka 裡面觸發一條消息,類似實現 trigger 的功能。binlog 本身是描述成一種通用的 protobuf 格式,也可以用來驅動流式計算引擎,來實現一些非同步/流式分析需求。Binlog 的使用場景非常廣泛,可以在實際業務中靈活發揮。

另外介紹一個工具就是 Lightning,Lightning可能大家都沒有用到過,因為我們還在最後的測試和優化階段,這是一個快速的 TiDB 導入工具,之前我們提供的工具是 MyDumper,MyDumper 是 MySQL 通用的一個數據導出的工具。它同時還有一個 MyLoader,我們在這個基礎上又做了一個 TiDB Loader,但這個東西本質上還是去執行 SQL。就是說 MyDumper 輸出的數據文件是很多的 SQL 文本。那麼用 Loader 導入到 TiDB 這個過程中大家可能會覺得導數據比較慢。這是因為這種方式的數據導入,TiKV 底層存儲的 region 要不斷的分裂和搬移,而且一般順序寫數據,表的主鍵往往是遞增的,這樣會導致寫入熱點,不能同時把所有 TiKV 節點都調動起來,失去了分散式的優勢。那麼 Lightning 是怎麼做的呢?首先我們會直接把輸入的數據格式繞過 SQL 解析器和優化器直接轉換成有序的 KV 鍵值對,並分批進行處理,根據 PD 預先計算好新插入數據的 Region 分布,然後直接生成 SST 文件 Ingest 到 TiKV 中,非常接近物理數據導入。我們在內部測試比之前的 Loader 方式要快 7 到 10 倍,1T 的數據將近在 5 個小時之內完成導入,預計很快會跟大家見面。

MyDumper 格式的文件作為輸入,首先完成 SQL 到 KV 的轉換,它是由若干分散式 worker 來完成,多機並行執行。同時繞過了優化器,生成連續的 KV 流,再通過一個專門的 Ingest Server 對 KV 進行全局排序。同時可以預計算 region,通過 PD 提前安排調度到哪個節點,所以整個的流程是非常高效的。

接下來介紹一個我們商業化工具,叫作 Wormhole。這個可以理解為是一個帶控制面板的 Syncer,但比 Syncer 強大。它支持多源多目的地的數據同步。而且本身也是分散式結構,具有高可用、並行執行的特點。另外它對於分庫分表支持的更好,配置可視化。在同步前檢查也更為嚴格,比如說同步 MySQL,會提前檢查表結構和 TiDB 的兼容性,是否開啟 row 模式的 binlog 等等,避免在運行過程中發現了再報異常。另外 Wormhole 也支持一些簡單的 ETL 轉換規則,比如在同步過程中對錶的某些欄位進行簡單映射計算和 UDF。比如對於分庫分表的合併,如果每張分表都有自己的自增主鍵,合表之後插入 TiDB 就可能遇到主鍵衝突。Wormhole 通過配置就可以完成主鍵的合併,也可以新增一個欄位作為真正的主鍵,原表的主鍵保留名字,去掉唯一性約束。

我截了一些界面的圖,可以看到整個數據同步過程中的進度,包括全量、增量的同步速度,以及我隨時可以把它暫停下來,或者進行一些特定的操作。對於多源/目的地這樣同步,像這個配置,我可以直接把資料庫裡面的表結構全部讀出來,用在界面上就可以決定同步表和資料庫以及欄位映射關係。

接下來第三部分,說說 TiDB 的數據可視化。TiDB Vision 這個項目是開源的,我們會在 PD 提供的介面上,來實現數據可視化。

從圖中可以清楚的看到在不同節點上 region 的分布,以及 region leader 的關係。圖中環上的每一段,代表一個 TiKV store。每個 store 的每一個小格代表一個 region,綠色代表是 leader ,中間的這些線段在運行過程中是有動畫效果的,當 Leader 發生分裂,遷移,還有 Leader transfer,都有不同顏色的動畫來表示。從而反映一個真實 PD 產生調度的過程,我們可以通過可視化很直觀的看到。另外就是熱點,這個圖裡可能沒有體現,如果某一個 region 出現熱點,在界面上就可以看到一些紅色的點。另外,這個邊緣展示的是每個 PD 調度的一些網路流量,TiKV 的一些流量的信息我們也是實時的展示。如果某一個結點掛了,在這個邊緣,它會有一定的顏色表示,比如說 TiKV 下線,熟悉 TiDB 的人知道,下線 TiKV 並不是立即就下線了,它會先變成下線中,然後變成 Tombstone 的狀態,這個在圖上都可以直觀的反映出來。這個工具非常簡單,就在 TiDB Vision 開源項目,有興趣的同學,可以給 TiDB 做更多的皮膚。讓這個展示更 cool,對業務監控更有幫助。

這個是我們在做的一個企業版的 Dashboard,這個可能跟大家看到的 Grafana 還有現有開源的界面不太相同,這裡截了一些局部的圖。大家可以看到,每個節點上面每個進程的狀態,包括節點運行時日誌,和服務健康狀態。通過 Dashboard 就可以把整個的集群的拓撲和運行狀態,全部展示出來。在這個界面它可以選擇去創建多少個 TiDB 多少個 TiKV 節點,並且選擇規格。左邊可以選擇升級的 TiDB 組件版本,完成滾動升級這樣的事情。

最後說一下 TiDB 的監控。監控我們後台用的 Prometheus 這個非常出名的項目,通過它來做存儲資料庫各個服務的 metrics。每個 TiDB、TiKV 組件都會把自己的狀態上報到 Prometheus(實際是 pull 的模式),我們通過 Node Exporter 來採集每台主機的狀態。而對於 K8s 集群,是通過 cAdvisor 進行收集,把 metrics 在 Prometheus 進行匯總。通過 Grafana 來做監控和可視化。我們配置好的 Grafana 面板點擊編輯按鈕,都可以看到對應的 Prometheus 查詢表達式,通過一種類似 SQL 的查詢語句,你就可以很方便的從 Prometheus 拉取監控數據然後對接到自己的監控平台。 Alert manager 也是 Prometheus 生態裡面的一個工具,它可以去接受 Prometheus 發出的報警事件,然後通過各種報警方式推送出來。日誌方面我們也是用比較流行的 EFK 套件。在 K8s 集群中,採集每個 Pod 的日誌,存放到 ES 裡面再通過 Kibana 進行展示。

這個是監控的幾個截圖,這個大家可能都比較熟悉了。

最後簡單聊一下 TiDB 生態,因為 TiDB 最大的優勢是兼容 MySQL 協議。所以不光是命令行工具,包括比如 MySQL 自己的 MySQL Workbench 這樣的工具,還有大家用傳統的 Navicat 這樣的產品工具,還有就是一個老牌的 phpMyAdmin 這樣的 Web 管理工具,都可以直接連到一個 TiDB 實例。我們同時也在不斷的優化 TiDB 兼容性,因為畢竟它跟 MySQL 有些區別。像這些工具,它可能會去讀 MySQL 的一些系統表,我們會盡量會跟 MySQL 保持兼容。還有一些很方便的功能,比如把 schema 抓出來,繪製 ER 圖,其實我們也希望在 TiDB 上跑的很順暢。這樣習慣使用 MySQL 各種管理工具的用戶,可以非常平滑的切換到 TiDB。

我今天介紹的內容主要就這些了,謝謝大家!


推薦閱讀:

資料庫管理系統(一): 並發控制簡介
從國內哪些公司可以買到比較靠譜的 POI 資料庫?
如何理解並正確使用MySql索引
雲資料庫UDB的三重境界
七周成為數據分析師:SQL,從熟練到掌握

TAG:資料庫 | TiDB | 工具 |