TiKV 源碼解析系列文章(十四)Coprocessor 概覽

做者: Shirlybash

本文將簡要介紹 TiKV Coprocessor 的基本原理,面向想要了解 TiKV 數據讀取執行過程的同窗,同時也面向想對該模塊貢獻代碼的同窗。閱讀本文前,建議讀者對 TiDB 總體架構有所瞭解,先閱讀三篇文章瞭解 TiDB 技術內幕:說存儲說計算談調度網絡

什麼是 Coprocessor

熟悉 TiDB 總體框架 的同窗可能記得,TiDB 是無狀態的,數據存儲在 TiKV 層。當 TiDB 在收到一個來自客戶端的查詢請求時,會向 TiKV 獲取具體的數據信息。那麼一個讀請求最樸素的處理過程以下:架構

首先須要確定的是這種方式當然能解決問題,可是性能如何呢?咱們來一塊兒分析一下:框架

  1. TiKV 將全部數據返回,網絡開銷太大。
  2. TiDB 須要計算全部數據,CPU 消耗很大,相對的,TiKV 卻並無什麼計算,很閒。

看到以上問題後,聰明如你,可能很容易就想到,能不能讓 TiKV 把本身負責的那部分數據作一次計算,再返回給 TiDB 呢?異步

有何不可呢?函數

TiKV 讀取數據並計算的模塊,咱們定義爲 Coprocessor,該概念靈感來自於 HBase,目前在 TiDB 中的實現相似於 HBase 中的 Coprocessor 的 Endpoint 部分,也可類比 MySQL 存儲過程。性能

有了 Coprocessor 後,從宏觀看一個讀請求是如何下發到 TiKV 的呢?如下面的請求爲例:優化

如圖,以上查詢語句在 TiDB 中處理以下:spa

  1. TiDB 收到查詢語句,對語句進行分析,計算出物理執行計劃,組織稱 TiKV 的 Coprocessor 請求。
  2. TiDB 將該 Coprocessor 請求根據數據的分佈,分發到全部相關的 TiKV 上。
  3. TiKV 在收到該 Coprocessor 請求後,根據請求算子對數據進行過濾聚合,而後返回給 TiDB。
  4. TiDB 在收到全部數據的返回結果後,進行二次聚合,並將最終結果計算出來,返回給客戶端。

主要功能及處理概覽

TiKV Coprocessor 處理的讀請求目前主要分類三種:線程

  • DAG:執行物理算子,爲 SQL 計算出中間結果,從而減小 TiDB 的計算和網絡開銷。這個是絕大多數場景下 Coprocessor 執行的任務。
  • Analyze:分析表數據,統計、採樣表數據信息,持久化後被 TiDB 的優化器採用。
  • CheckSum:對錶數據進行校驗,用於導入數據後一致性校驗。

那麼 TiKV 在收到 Coprocessor 請求後,什麼時候區分這三種請求的呢?

請求到了 TiKV 層,處理過程以下:

  • 由 gRPC server 接收並將請求分發給 Coprocessor Endpoint 進行處理。
  • Endpoint 在收到請求後,根據請求的優先級,將請求分發給對應的線程池。
  • 全部請求會先異步從存儲層獲取 snapshot,而後開始真正的處理階段。
  • 根據請求的不一樣類型,構造不一樣的 Handler 進行數據的處理。

目前 Coprocessor 支持的三種接口中,後面兩種接口相對比較簡單,而 DAG 是裏面最複雜也是最經常使用的,因此本文後續將重點介紹 DAG 類請求。

DAG Request 概覽

DAG 顧名思義,是由一系列算子組成的有向無環圖,算子在代碼中稱爲 Executors。

目前 DAG 請求主要實現了兩種計算模型:

  • 火山模型:每一個算子按行按需吐出,3.0 以後開始棄用。
  • 向量化計算模型:每一個算子批量化處理數據,3.0 以後開始推廣。

在目前的 TiKV master 上,處於火山模型向向量化模型的過分階段,於是兩種計算模型同時存在。TiKV 收到請求時,會優先檢測是否可走向量化模型,若部分功能在向量化模型中沒有實現,則走舊的計算模型,具體處理邏輯流程以下:

相關代碼在:src/coprocessor/dag/mod.rs

由於火山模型已在被棄用中,因此下文咱們只講向量化計算模型。

算子概覽

在向量化計算模型中,全部算子都實現了 BatchExecutor接口,其主要定義了一個 get_batch 的函數:

pub trait BatchExecutor: Send {
   fn next_batch(&mut self, scan_rows: usize) -> BatchExecuteResult;
}

pub struct BatchExecuteResult {
   pub physical_columns: LazyBatchColumnVec,
   pub logical_rows: Vec<usize>,
   pub is_drained: Result<bool, Error>,
   ...
}
複製代碼

參數說明:

  • next_batchscan_rows 由上層控制,因爲掃的數據過多會慢,所以該數字從 32 倍增到 1024。

  • 返回值 BatchExecuteResult 中,因爲返回了一批空數據不表明全部數據都處理完畢了,例如可能只是全被過濾,於是使用單獨字段表示全部數據處理完畢。

目前 TiKV 支持的算子主要有如下幾類。

TableScan

  • 定義:根據指定主鍵範圍掃表數據,並過濾出一部分列返回。它只會做爲最底層算子出現,從底層 KV 獲取數據。

  • 源碼路徑:components/tidb_query/src/batch/executors/table_scan_executor.rs

  • 案例:select col from t

IndexScan

  • 定義:根據指定索引返回掃索引數據,並過濾出一部分索引列返回。它只會做爲最底層算子出現,從底層 KV 獲取數據。

  • 源碼路徑:components/tidb_query/src/batch/executors/index_scan_executor.rs

  • 案例:select index from t

Selection

  • 定義:對底層算子的結果按照過濾條件進行過濾,其中這些條件由多個表達式組成。

  • 源碼路徑:components/tidb_query/src/batch/executors/selection_executor.rs

  • 案例:select col from t where a+b=10

Limit

  • 定義:從底層算子吐出的數據中,限定返回若干行。

  • 源碼路徑:components/tidb_query/src/batch/executors/limit_executor.rs

  • 案例:select col from t limit 10

TopN

  • 定義:按照給定表達式進行排序後,取出前若干行數據。

  • 源碼路徑:components/tidb_query/src/batch/executors/top_n_executor.rs

  • 案例:select col from t order by a+1 limit 10

Aggregation

  • 定義:按照給定表達式進行分組、聚合。

  • 源碼路徑:components/tidb_query/src/batch/executors/*_aggr_executor.rs

  • 案例: select count(1) from t group by score + 1

混合使用各個算子

綜上,各個算子之間能夠按照如下方式任意組合,以下圖所示:

案例:select count(1) from t where age>10

小結

因爲篇幅緣由,本文只是講了一些 Coprocessor 的概要,讀者對此有個概念便可。後續咱們將推出該模塊相關的更多更深的源碼細節分析,歡迎你們繼續閱讀並給出建設性的改進意見。

原文閱讀pingcap.com/blog-cn/tik…

相關文章
相關標籤/搜索