TiDB 原始碼閱讀系列文章(二)初識 TiDB 原始碼
本文為 TiDB 原始碼閱讀系列文章的第二篇,第一篇文章介紹了 TiDB 整體的架構,知道 TiDB 有哪些模組,分別是做什麼的,從哪裡入手比較好,哪些可以忽略,哪些需要仔細閱讀。
這篇文章是一篇入門文件,難度係數比較低,其中部分內容可能大家在其他渠道已經看過,不過為了內容完整性,我們還是會放在這裡。
TiDB 架構
本次 TiDB 原始碼之旅從這幅簡單的架構圖開始,這幅圖很多人都看過,我們可以用一句話來描述這個圖:『TiDB 是一個支援 MySQL 協議,以某種支援事務的分散式 KV 儲存引擎為底層儲存的 SQL 引擎』。從這句話可以看出有三個重要的事情,第一是如何支援 MySQL 協議,與 Client 互動,第二是如何與底層的儲存引擎打交道,存取資料,第三是如何實現 SQL 的功能。本篇文章會先介紹一些 TiDB 有哪些模組及其功能簡要介紹,然後以這三點為線索,將這些模組串聯起來。
程式碼簡介
TiDB 原始碼完全託管在 Github 上,從專案主頁可以看到所有資訊。整個專案使用 Go 語言開發,按照功能模組分了很多 Package,通過一些依賴分析工具,可以看到專案內部包之間的依賴關係。
大部分包都以介面的形式對外提供服務,大部分功能也都集中在某個包中,不過有一些包提供了非常基礎的功能,會被很多包依賴,這些包需要特別注意。
專案的 main 檔案在 tidb-server/main.go,這裡面定義了服務如何啟動。整個專案的 Build 方法可以在 Makefile 中找到。
除了程式碼之外,還有很多測試用例,可以在 xx_test.go 中找到。另外 cmd
模組介紹
TiDB 的模組非常多,這裡做一個整體介紹,大家可以看到每個模組大致是做什麼用的,想看相關功能的程式碼是,可以直接找到對應的模組。
Package | Introduction |
---|---|
ast | 抽象語法樹的資料結構定義,例如 SelectStmt 定義了一條 Select 語句被解析成什麼樣的資料結構 |
cmd/benchdb | 簡單的 benchmark 工具,用於效能優化 |
cmd/benchfilesort | 簡單的 benchmark 工具,用於效能優化 |
cmd/benchkv | Transactional KV API benchmark 工具,也可以看做 KV 介面的使用樣例 |
cmd/benchraw | Raw KV API benchmark 工具,也可以看做不帶事務的 KV 介面的使用樣例 |
cmd/importer | 根據表結構以及統計資訊偽造資料的工具,用於構造測試資料 |
config | 配置檔案相關邏輯 |
context | 主要包括 Context 介面,提供一些基本的功能抽象,很多包以及函式都會依賴於這個介面,把這些功能抽象為介面是為了解決包之間的依賴關係 |
ddl | DDL 的執行邏輯 |
distsql | 對分散式計算介面的抽象,通過這個包把 Executor 和 TiKV Client 之間的邏輯做隔離 |
domain | domain 可以認為是一個儲存空間的抽象,可以在其中建立資料庫、建立表,不同的 domain 之間,可以存在相同名稱的資料庫,有點像 Name Space。一般來說單個 TiDB 例項只會建立一個 Domain 例項,其中會持有 information schema 資訊、統計資訊等。 |
executor | 執行器相關邏輯,可以認為大部分語句的執行邏輯都在這裡,比較雜,後面會專門介紹 |
expression | 表示式相關邏輯,包括各種運算子、內建函式 |
expression/aggregation | 聚合表示式相關的邏輯,比如 Sum、Count 等函式 |
infoschema | SQL 元資訊管理模組,另外對於 Information Schema 的操作,都會訪問這裡 |
kv | KV 引擎介面以及一些公用方法,底層的儲存引擎需要實現這個包中定義的介面 |
meta | 利用 structure 包提供的功能,管理儲存引擎中儲存的 SQL 元資訊,infoschema/DDL 利用這個模組訪問或者修改 SQL 元資訊 |
meta/autoid | 用於生成全域性唯一自增 ID 的模組,除了用於給每個表的自增 ID 之外,還用於生成 |
metrics | Metrics 相關資訊,所有的模組的 Metrics 資訊都在這裡 |
model | SQL 元資訊資料結構,包括 DBInfo / TableInfo / ColumnInfo / IndexInfo 等 |
mysql | MySQL 相關的常量定義 |
owner | TiDB 叢集中的一些任務只能有一個例項執行,比如非同步 Schema 變更,這個模組用於多個 tidb-server 之間協調產生一個任務執行者。每種任務都會產生自己的執行者。 |
parser | 語法解析模組,主要包括詞法解析 (lexer.go) 和語法解析 (parser.y),這個包對外的主要介面是 Parse(),用於將 SQL 文字解析成 AST |
parser/goyacc | 對 GoYacc 的包裝 |
parser/opcode | 關於操作符的一些常量定義 |
perfschema | Performance Schema 相關的功能,預設不會啟用 |
plan | 查詢優化相關的邏輯 |
privilege | 使用者許可權管理介面 |
privilege/privileges | 使用者許可權管理功能實現 |
server | MySQL 協議以及 Session 管理相關邏輯 |
sessionctx/binloginfo | 向 Binlog 模組輸出 Binlog 資訊 |
sessionctx/stmtctx | Session 中的語句執行時所需要的資訊,比較雜 |
sessionctx/variable | System Variable 相關程式碼 |
statistics | 統計資訊模組 |
store | 儲存引擎相關邏輯,這裡是儲存引擎和 SQL 層之間的互動邏輯 |
store/mockoracle | 模擬 TSO 元件 |
store/mockstore | 例項化一個 Mock TiKV 的邏輯,主要方法是 NewMockTikvStore,把這部分邏輯從 mocktikv 中抽出來是避免迴圈依賴 |
store/mockstore/mocktikv | 在單機儲存引擎上模擬 TiKV 的一些行為,主要作用是本地除錯、構造單元測試以及指導 TiKV 開發 Coprocessor 相關邏輯 |
store/tikv | TiKV 的 Go 語言 Client |
store/tikv/gcworker | TiKV GC 相關邏輯,tidb-server 會根據配置的策略向 TiKV 傳送 GC 命令 |
store/tikv/oracle | TSO 服務介面 |
store/tikv/oracle/oracles | TSO 服務的 Client |
store/tikv/tikvrpc | TiKV API 的一些常量定義 |
structure | 在 Transactional KV API 上定義的一層結構化 API,提供 List/Queue/HashMap 等結構 |
table | 對 SQL 的 Table 的抽象 |
table/tables | 對 table 包中定義的介面的實現 |
tablecodec | SQL 到 Key-Value 的編解碼,每種資料型別的具體編解碼方案見 codec 包 |
terror | TiDB 的 error 封裝 |
tidb-server | 服務的 main 方法 |
types | 所有和型別相關的邏輯,包括一些型別的定義、對型別的操作等 |
types/json | json 型別相關的邏輯 |
util | 一些實用工具,這個目錄下面包很多,這裡只會介紹幾個重要的包 |
util/admin | TiDB 的管理語句( Admin 語句)用到的一些方法 |
util/charset | 字符集相關邏輯 |
util/chunk | Chunk 是 TiDB 1.1 版本引入的一種資料表示結構。一個 Chunk 中儲存了若干行資料,在進行 SQL 計算時,資料是以 Chunk 為單位在各個模組之間流動 |
util/codec | 各種資料型別的編解碼 |
x-server | X-Protocol 實現 |
從哪裡入手
粗看一下 TiDB 有 80 個包,讓人覺得無從下手,不過並不是所有的包都很重要,另外一些功能只會涉及到少量包,從哪裡入手去看原始碼取決於看原始碼的目的。
如果是想了解某一個具體的功能的實現細節,那麼可以參考上面的模組簡介,找到對應的模組即可。
如果是相對原始碼有全面的瞭解,那麼可以從 tidb-server/main.go 入手,看 tidb-server 是如何啟動,如何等待並處理使用者請求。再跟著程式碼一直走,看 SQL 的具體執行過程。另外一些重要的模組,需要看一下,知道是如何實現的。輔助性的模組,可以選擇性的看一下,有大致的印象即可。
重要模組
在全部 80 個模組中,下面幾個模組是最重要的,希望大家能仔細閱讀,針對這些模組,我們也會用專門的文章來講解,等所有的文章都 Ready 後,我將下面的表格中的 TODO 換成對應的文章連連結。
Package | Related Articles |
---|---|
plan | TODO |
expression | TODO |
executor | TODO |
distsql | TODO |
store/tikv | TODO |
ddl | TODO |
tablecodec | TODO |
server | TODO |
types | TODO |
kv | TODO |
tidb | TODO |
輔助模組
除了重要的模組之外,餘下的是輔助模組,但並不是說這些模組不重要,只是鎖這些模組並不在 SQL 執行的關鍵路徑上,我們也會用一定的篇幅描述其中的大部分包。
SQL 層架構
這幅圖比上一幅圖詳細很多,大體描述了 SQL 核心模組,大家可以從左邊開始,順著箭頭的方向看。
Protocol Layer
最左邊是 TiDB 的 Protocol Layer,這裡是與 Client 互動的介面,目前 TiDB 只支援 MySQL 協議,相關的程式碼都在 server
包中。
這一層的主要功能是管理客戶端 connection,解析 MySQL 命令並返回執行結果。具體的實現是按照 MySQL 協議實現,具體的協議可以參考 MySQL 協議文件。這個模組我們認為是當前實現最好的一個 MySQL 協議元件,如果大家的專案中需要用到 MySQL 協議解析、處理的功能,可以參考或引用這個模組。
連線建立的邏輯在 server.go 的 Run() 方法中,主要是下面兩行:
236: conn, err := s.listener.Accept()
258: go s.onConn(conn)
單個 Session 處理命令的入口方法是呼叫 clientConn 類的 dispatch 方法,這裡會解析協議並轉給不同的處理函式。
SQL Layer
大體上講,一條 SQL 語句需要經過,語法解析–>合法性驗證–>制定查詢計劃–>優化查詢計劃–>根據計劃生成查詢器–>執行並返回結果 等一系列流程。這個主幹對應於 TiDB 的下列包:
Package | 作用 |
---|---|
tidb | Protocol 層和 SQL 層之間的介面 |
parser | 語法解析 |
plan | 合法性驗證 + 制定查詢計劃 + 優化查詢計劃 |
executor | 執行器生成以及執行 |
distsql | 通過 TiKV Client 向 TiKV 傳送以及彙總返回結果 |
store/tikv | TiKV Client |
KV API Layer
TiDB 依賴於底層的儲存引擎提供資料的存取功能,但是並不是依賴於特定的儲存引擎(比如 TiKV),而是對儲存引擎提出一些要求,滿足這些要求的引擎都能使用(其中 TiKV 是最合適的一款)。
最基本的要求是『帶事務的 Key-Value 引擎,且提供 Go 語言的 Driver』,再高階一點的要求是『支援分散式計算介面』,這樣 TiDB 可以把一些計算請求下推到 儲存引擎上進行。
這些要求都可以在 kv
這個包的介面中找到,儲存引擎需要提供實現了這些介面的 Go 語言 Driver,然後 TiDB 利用這些介面操作底層資料。
對於最基本的要求,可以重點看這幾個介面:
有了上面這些介面,可以對資料做各種所需要的操作,完成全部 SQL 功能,但是為了更高效的進行運算,我們還定義了一個高階計算介面,可以關注這三個 Interface/struct :
Client:向下層傳送請求以及獲取下層儲存引擎的計算能力
小結
至此,讀者已經來了解了 TiDB 的原始碼結構以及三個主要部分的架構,更詳細的內容會在後面的章節中詳細描述。
作者:申礫