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