摘要:拿到返回結(jié)果進一步的進行計算處理。比較痛苦的經(jīng)歷不支持,我們就只好寫內(nèi)置函數(shù),就把另外一個模塊拖下來,自己修改加上語法,然后在加上自己設(shè)計的內(nèi)置函數(shù)。其次就是涉及的的源碼模塊很多,從優(yōu)化器執(zhí)行器內(nèi)置函數(shù)以及各種各樣的結(jié)構(gòu)。
本文作者是來自 CC 組的蘭海同學(xué),他們的項目《讓 TiDB 訪問多種數(shù)據(jù)源》在本屆 TiDB Hackathon 2018 中獲得了二等獎。該項目可以讓 TiDB 支持多種外部數(shù)據(jù)源的訪問,針對不同數(shù)據(jù)源的特點會不同的下推工作,使 TiDB 成為一個更加通用的數(shù)據(jù)庫查詢優(yōu)化和計算平臺。
我們隊伍是由武漢大學(xué)在校學(xué)生組成。我們選擇的課題是讓 TiDB 接入若干外部的數(shù)據(jù)源,使得 TiDB 稱為一個更加通用的查詢優(yōu)化和計算平臺。
為什么選這個課題剛開始我們選擇課題是 TiDB 執(zhí)行計劃的實時動態(tài)可視化。但是填了報名單后,TiDB Robot 回復(fù)我們說做可視化的人太多了。我們擔(dān)心和別人太多沖突,所以咨詢了導(dǎo)師的意見,改成了 TiDB 外部數(shù)據(jù)源訪問。這期間也閱讀了 F1 Query 和 Calcite 論文,看了東旭哥(PingCAP CTO)在 PingCAP 內(nèi)部的論文閱讀的分享視頻。感覺寫一個簡單 Demo,還是可行的。
系統(tǒng)架構(gòu)和效果展示如上圖所示,TiDB 通過 RPC 接入多個不同的數(shù)據(jù)源。TiDB 發(fā)送利用 RPC 發(fā)送請求給遠(yuǎn)端數(shù)據(jù)源,遠(yuǎn)端數(shù)據(jù)源收到請求后,進行查詢處理,返回結(jié)果。TiDB 拿到返回結(jié)果進一步的進行計算處理。
我們通過定義一張系統(tǒng)表 foreign_register(table_name,source_type,rpc_info) 記錄一個表上的數(shù)據(jù)具體來自哪種數(shù)據(jù)源類型,以及對應(yīng)的 RPC 連接信息。對于來自 TiKV 的我們不用在這個表中寫入,默認(rèn)的數(shù)據(jù)就是來自 TiKV。
我們想訪問一張 PostgreSQL(后面簡稱為 PG)上的表:首先,我們在 TiDB 上定義一個表(記為表 a),然后利用我們 register_foreign(a,postgresql,ip#port#table_name) 注冊相關(guān)信息。之后我們就可以通過 select * from a 來讀取在 PG 上名為 table_name 的表。
我們在設(shè)計各個數(shù)據(jù)源上數(shù)據(jù)訪問時,充分考慮各個數(shù)據(jù)源自身的特點。將合適的操作下推到具體的數(shù)據(jù)源來做。例如,PG 本身就是一個完整的數(shù)據(jù)庫系統(tǒng),我們支持投影、條件、連接下推給 PG 來做。Redis 是一個內(nèi)存鍵值數(shù)據(jù)庫,我們考慮到其 Get 以及用正則來匹配鍵值很快,我們將在 Key 值列的點查詢以及模糊匹配查詢都推給了 Redis 來做,其他條件查詢我們就沒有進行下推。
具體的運行效果如下:
如圖所示,我們在遠(yuǎn)程開了 3 個 RPC Server,負(fù)責(zé)接收 TiDB 執(zhí)行過程中的外部表請求,并在內(nèi)部的系統(tǒng)表中進行了注冊三張表,并在 TiDB 本地進行了模式的創(chuàng)建——分別是remotecsv,remoteredis,remotepg,還有一張本地 KV Store 上的 localkv 表。我們對 4 張表進行 Join 操作,效果如圖所示,說明如下。
1. 遠(yuǎn)程 csv 文件我們不做選擇下推,所以可以發(fā)現(xiàn) csv 上的條件還是在 root(即本地)上做。
2. 遠(yuǎn)程的 PG 表,我們會進行選擇下推,所以可以發(fā)現(xiàn) PG 表的 selection 被推到了 PG 上。
3. 遠(yuǎn)程的 Redis 表,我們也會進行選擇下推,同時還可以包括模型查詢條件(Like)的下推。
P.S. 此外,對于 PostgreSQL 源上兩個表的 Join 操作,我們也做了Join 的下推,Join 節(jié)點也被推送到了 PostgreSQL 來做,具體的圖示如下:
如何做的由于項目偏硬核的,需要充分理解 TiDB 的優(yōu)化器,執(zhí)行器等代碼細(xì)節(jié)。所以在比賽前期,我們花了兩三天去研讀 TiDB 的優(yōu)化器,執(zhí)行器代碼,弄清楚一個簡單的 Select 語句扔進 TiDB 是如何進行邏輯優(yōu)化,物理優(yōu)化,以及生成執(zhí)行器。之前我們對 TiDB 這些細(xì)節(jié)都不了解,硬著去啃。發(fā)現(xiàn) TiDB 生成完執(zhí)行器,會調(diào)用一個 Open 函數(shù),這個函數(shù)還是一個遞歸調(diào)用,最終到 TableReader 才發(fā)出數(shù)據(jù)讀取請求,并且已經(jīng)開始拿返回結(jié)果。這個和以前分析的數(shù)據(jù)庫系統(tǒng)還有些不同。前期為了檢驗我們自己對 TiDB 的執(zhí)行流程理解的是否清楚,我們嘗試這去讓 TiDB 讀取本地 csv 文件。
比賽正式開始,我們一方面完善 csv,不讓其進行條件下推,因為我們遠(yuǎn)端 RPC 沒有處理條件的能力,我們修改了邏輯計劃的條件下推規(guī)則,遇到數(shù)據(jù)源是 csv 的,我們拒絕條件下推。另一方面,首先得啃下硬骨頭 PostgreSQL。我們考慮了兩種方案,第一種是拿到 TiDB 的物理計劃后,我們將其轉(zhuǎn)換為 SQL,然后發(fā)給 PG;第二種方案我們直接將 TiDB 的物理計劃序列化為 PG 的物理計劃,發(fā)給 PG。我們考慮到第二種方案需要給 PG 本身加接受物理計劃的鉤子,就果斷放棄。可能兩天都費在該 PG 代碼上了。我們首先實現(xiàn)了 select * from pgtable。主要修改了增加 pgSelectResult 結(jié)構(gòu)體實現(xiàn)對應(yīng)的結(jié)構(gòu)體。通過看該結(jié)構(gòu)體以及其對應(yīng)接口函數(shù),大家就知道如何去讀取一個數(shù)據(jù)源上的數(shù)據(jù),以及是如何做投影下推。修改 Datasource 數(shù)據(jù)結(jié)構(gòu)增加對數(shù)據(jù)源類型,RPC 信息,以及條件字符串,在部分物理計劃內(nèi),我們也增加相關(guān)信息。同時根據(jù)數(shù)據(jù)源信息,在 (e*TableReaderExecutor)buildResp 增加對來源是 PG 的表處理。
接著我們開始嘗試條件下推:select * from pgtable where … 將 where 推下去。我們發(fā)現(xiàn)第一問題:由于我們的注冊表里面沒有記錄外部源數(shù)據(jù)表的模式信息導(dǎo)致,下推去構(gòu)建 SQL 的時候根本拿不到外部數(shù)據(jù)源 PG 上正確的屬性名。所以我們暫時保證 TiDB 創(chuàng)建的表模式與 PG 創(chuàng)建的表模式完全一樣來解決這個問題。條件下推,我們對條件的轉(zhuǎn)換為字符串在函數(shù) ExpressionToString 中,看該函數(shù)調(diào)用即可明白是如何轉(zhuǎn)換的。當(dāng)前我們支持等于、大于、小于三種操作符的下推。
很快就到了 1 號下午了,我們主要工作就是進行 Join下推 的工作。Join 下推主要是當(dāng)我們發(fā)現(xiàn)兩個 Join 的表都來來自于同一個 PG 實例時,我們就將該 Join 下推給 PG。我們增加一種 Join 執(zhí)行器:PushDownJoinExec。弄完 Join 已經(jīng)是晚上了。而且中間還遇到幾個 Bug,首先,PG 等數(shù)據(jù)源沒有一條結(jié)果滿足時的邊界條件沒有進行檢查,其次是,在 Join 下推時,某些情況下 Join 條件未必都是在 On 子句,這個時候需要考慮 Where 子句的信息。最后一個,如果使得連接和條件同時下推沒有問題。因為不同表的相同屬性需要進行區(qū)分。主要難點就是對各個物理計劃的結(jié)構(gòu)體中的解析工作。
到了晚上,我們準(zhǔn)備開始著手接入 Redis。考慮到 Redis 本身是 KV 型,對于給定 Key 的 Get 以及給定 Key 模式的匹配是很快。我們直接想到對于 Redis,我們允許 Key 值列上的條件下推,讓 Redis 來做過濾。因為 Redis 是 API 形式,我們多帶帶定義一個簡單請求協(xié)議,來區(qū)別單值,模糊,以及全庫查詢?nèi)N基本情況,見 RequestRedis 定義。Redis 整體也像是 PG 一樣的處理,主要沒有 Join 下推這一個比較復(fù)雜的點。
我們之后又對 Explain 部分進行修改,使得能夠打印能夠反映我們現(xiàn)在加入外部數(shù)據(jù)源后算子真實的執(zhí)行情況,可以見 explainPlanInRowFormat 部分代碼。之后我們開始進行測試每個數(shù)據(jù)源上的,以及多個數(shù)據(jù)源融合起來進行測試。
不足之處1. 我們很多物理計劃都是復(fù)用 TiDB 本身的,給物理計劃加上很多附屬屬性。其實最好是將這些物理計劃多帶帶抽取出來形成一個,不去復(fù)用。
2. Cost 沒有進行細(xì)致考慮,例如對于 Join 下推,其實兩張 100 萬的表進行 Join 可能使得結(jié)果成為 1000 萬,那么網(wǎng)絡(luò)傳輸?shù)拇鷥r反而更大了。這些具體算子下推的代價還需要細(xì)致的考慮。
比較痛苦的經(jīng)歷1. TiDB 不支持 Create Funtion,我們就只好寫內(nèi)置函數(shù),就把另外一個 Parser 模塊拖下來,自己修改加上語法,然后在加上自己設(shè)計的內(nèi)置函數(shù)。
2. 最痛苦還是兩個方面,首先 Golang 語言,我們之前沒有用得很多,經(jīng)常遇到些小問題,例如 interface 的靈活使用等。其次就是涉及的 TiDB 的源碼模塊很多,從優(yōu)化器、執(zhí)行器、內(nèi)置函數(shù)以及各種各樣的結(jié)構(gòu)。雖然思路很簡單,但是改動的地方都很細(xì)節(jié)。
收獲比賽過程中,看到了非常多優(yōu)秀選手,以及他們酷炫的作業(yè),感覺還是有很長的路要走。Hackathon 的選手都好厲害,聽到大家噼里啪啦敲鍵盤的聲音,似乎自己也不覺得有多累了。人啊,逼一下自己,會感受到自己無窮的力量。通過這次活動,我們最終能夠靈活使用 Golang 語言,對 TiDB 整體也有了更深入的認(rèn)識,希望自己以后能夠稱為 TiDB 的代碼貢獻(xiàn)者。
最后非常感謝 PingCAP 這次組織的 Hackathon 活動,感謝導(dǎo)師團、志愿者,以及還有特別感謝導(dǎo)師張建的指導(dǎo)。
TiDB Hackathon 2018 共評選出六個優(yōu)秀項目,本系列文章將由這六個項目成員主筆,分享他們的參賽經(jīng)驗和成果。我們非常希望本屆 Hackathon 誕生的優(yōu)秀項目能夠在社區(qū)中延續(xù)下去,感興趣的小伙伴們可以加入進來哦。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/17842.html
摘要:拿到返回結(jié)果進一步的進行計算處理。比較痛苦的經(jīng)歷不支持,我們就只好寫內(nèi)置函數(shù),就把另外一個模塊拖下來,自己修改加上語法,然后在加上自己設(shè)計的內(nèi)置函數(shù)。其次就是涉及的的源碼模塊很多,從優(yōu)化器執(zhí)行器內(nèi)置函數(shù)以及各種各樣的結(jié)構(gòu)。 本文作者是來自 CC 組的蘭海同學(xué),他們的項目《讓 TiDB 訪問多種數(shù)據(jù)源》在本屆 TiDB Hackathon 2018 中獲得了二等獎。該項目可以讓 TiDB...
閱讀 2041·2021-11-12 10:36
閱讀 1912·2021-11-09 09:49
閱讀 2615·2021-11-04 16:12
閱讀 1161·2021-10-09 09:57
閱讀 3253·2019-08-29 17:24
閱讀 1925·2019-08-29 15:12
閱讀 1285·2019-08-29 14:07
閱讀 1300·2019-08-29 12:53