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