天天看點

9.TiDB資料庫的計算

SQL 層簡介

TiDB 的 SQL 層,即 TiDB Server,負責将 SQL 翻譯成 Key-Value 操作,将其轉發給共用的分布式 Key-Value 存儲層 TiKV,然後組裝 TiKV 傳回的結果,最終将查詢結果傳回給用戶端。

這一層的節點都是無狀态的,節點本身并不存儲資料,節點之間完全對等。

SQL 運算

最簡單的方案就是通過上一節所述的表資料與 Key-Value 的映射關系方案,将 SQL 查詢映射為對 KV 的查詢,再通過 KV 接口擷取對應的資料,最後執行各種計算。

比如 select count(*) from user where name = "TiDB" 這樣一個 SQL 語句,它需要讀取表中所有的資料,然後檢查 name 字段是否是 TiDB,如果是的話,則傳回這一行。具體流程如下:

  1. 構造出 Key Range:一個表中所有的 RowID 都在 [0, MaxInt64) 這個範圍内,使用 0 和 MaxInt64 根據行資料的 Key 編碼規則,就能構造出一個 [StartKey, EndKey)的左閉右開區間。
  2. 掃描 Key Range:根據上面構造出的 Key Range,讀取 TiKV 中的資料。
  3. 過濾資料:對于讀到的每一行資料,計算 name = "TiDB" 這個表達式,如果為真,則向上傳回這一行,否則丢棄這一行資料。
  4. 計算 Count(*):對符合要求的每一行,累計到 Count(*) 的結果上面。

整個流程示意圖如下:

9.TiDB資料庫的計算

這個方案是直覺且可行的,但是在分布式資料庫的場景下有一些顯而易見的問題:

  • 在掃描資料的時候,每一行都要通過 KV 操作從 TiKV 中讀取出來,至少有一次 RPC 開銷,如果需要掃描的資料很多,那麼這個開銷會非常大。
  • 并不是所有的行都滿足過濾條件 name = "TiDB",如果不滿足條件,其實可以不讀取出來。
  • 符合要求的行的值并沒有什麼意義,實際上這裡隻需要有幾行資料這個資訊就行。

分布式 SQL 運算

為了解決上述問題,計算應該需要盡量靠近存儲節點,以避免大量的 RPC 調用。首先,SQL 中的謂詞條件 name = "TiDB" 應被下推到存儲節點進行計算,這樣隻需要傳回有效的行,避免無意義的網絡傳輸。然後,聚合函數 Count(*) 也可以被下推到存儲節點,進行預聚合,每個節點隻需要傳回一個 Count(*) 的結果即可,再由 SQL 層将各個節點傳回的 Count(*) 的結果累加求和。

以下是資料逐層傳回的示意圖:

9.TiDB資料庫的計算

SQL 層架構

通過上面的例子,希望大家對 SQL 語句的處理有一個基本的了解。實際上 TiDB 的 SQL 層要複雜得多,子產品以及層次非常多,下圖列出了重要的子產品以及調用關系:

9.TiDB資料庫的計算

使用者的 SQL 請求會直接或者通過 Load Balancer 發送到 TiDB Server,TiDB Server 會解析 MySQL Protocol Packet,擷取請求内容,對 SQL 進行文法解析和語義分析,制定和優化查詢計劃,執行查詢計劃并擷取和處理資料。資料全部存儲在 TiKV 叢集中,是以在這個過程中 TiDB Server 需要和 TiKV 互動,擷取資料。最後 TiDB Server 需要将查詢結果傳回給使用者。