摘要:當(dāng)我們正準(zhǔn)備做前期調(diào)研和設(shè)計(jì)的時(shí)候,主辦方把唐長(zhǎng)老拉去做現(xiàn)場(chǎng)導(dǎo)師,參賽規(guī)則規(guī)定導(dǎo)師不能下場(chǎng)比賽,囧,于是就這樣被被動(dòng)放了鴿子。川總早早來(lái)到現(xiàn)場(chǎng)。
本文作者是來(lái)自 TiBoys 隊(duì)的崔秋同學(xué),他們的項(xiàng)目 TBSSQL 在 TiDB Hackathon 2018 中獲得了一等獎(jiǎng)。序TiDB Batch and Streaming SQL(簡(jiǎn)稱 TBSSQL)擴(kuò)展了 TiDB 的 SQL 引擎,支持用戶以類似 StreamSQL 的語(yǔ)法將 Kafka、Pulsar 等外部數(shù)據(jù)源以流式表的方式接入 TiDB。通過(guò)簡(jiǎn)單的 SQL 語(yǔ)句,用戶可以實(shí)現(xiàn)對(duì)流式數(shù)據(jù)的過(guò)濾,流式表與普通表的 Join(比如流式事實(shí)表與多個(gè)普通維度表),甚至通過(guò) CREATE TABLE AS SELECT 語(yǔ)法將處理過(guò)的流式數(shù)據(jù)寫入普通表中。此外,針對(duì)流式數(shù)據(jù)的時(shí)間屬性,我們實(shí)現(xiàn)了基于時(shí)間窗口的聚合/排序算子,使得我們可以對(duì)流式數(shù)據(jù)進(jìn)行時(shí)間維度的聚合/排序。
算起來(lái)這應(yīng)該是第三次參加的 Hackathon 了,第一次參加的時(shí)候還是在小西天的豌豆莢,和東旭一起,做跨平臺(tái)數(shù)據(jù)傳輸?shù)墓ぞ撸瑑商煲灰?;第二次和奇叔一起?3W 咖啡,又是兩天一夜;這次在自己家舉辦 Hackathon 比賽,下定決心一定要佛性一些,本著能抱大腿就不單干的心態(tài),迅速?zèng)Q定拉唐長(zhǎng)老(唐劉)下水。接下來(lái)就計(jì)劃著折騰點(diǎn)啥,因?yàn)槲覀儍蓚€(gè)前端都不怎么樣,所以只能硬核一些,于是拍了兩個(gè)方案。
方案一:之前跟唐長(zhǎng)老合作過(guò)很長(zhǎng)一段時(shí)間,我們兩個(gè)對(duì)于測(cè)試質(zhì)量之類的事情也都非常關(guān)注,所以想著能不能在 Chaos 系統(tǒng)上做一些文章,把一些前沿的測(cè)試?yán)碚摵徒?jīng)驗(yàn)方法結(jié)合到系統(tǒng)里面來(lái),做一套通用的分布式系統(tǒng)測(cè)試框架,就像 Jepsen 那樣,用這套系統(tǒng)去測(cè)試和驗(yàn)證主流的開(kāi)源分布式項(xiàng)目。
方案二:越接近于業(yè)務(wù)實(shí)時(shí)性的數(shù)據(jù)處理越有價(jià)值,不管是 Kafka/KSQL,F(xiàn)link/Spark Streaming 都是在向著實(shí)時(shí)流計(jì)算領(lǐng)域方向進(jìn)行未來(lái)的探索。TiDB 雖然已經(jīng)能夠支持類 Real Time OLAP 的場(chǎng)景,但是對(duì)于更實(shí)時(shí)的流式數(shù)據(jù)處理方面還沒(méi)有合適的解決方案,不過(guò) TiDB 具有非常好的 Scale 能力,天然的能存儲(chǔ)海量的數(shù)據(jù)庫(kù)表數(shù)據(jù),所以在 Streaming Event 和 Table 關(guān)聯(lián)的場(chǎng)景下具有非常明顯的優(yōu)勢(shì)。如果在 TiDB 上能夠?qū)崿F(xiàn)一個(gè) Streaming SQL 的引擎,實(shí)現(xiàn) Batch/Streaming 的計(jì)算融合,那將會(huì)是一件非常有意思的事情。
因?yàn)榇?Hackathon 比賽主要是希望折騰一些新的東西,所以我們兩個(gè)簡(jiǎn)單討論完了之后還是傾向于方案二,當(dāng)然做不做的出來(lái)另說(shuō)。
當(dāng)我們正準(zhǔn)備做前期調(diào)研和設(shè)計(jì)的時(shí)候,Hackathon 主辦方把唐長(zhǎng)老拉去做現(xiàn)場(chǎng)導(dǎo)師,參賽規(guī)則規(guī)定導(dǎo)師不能下場(chǎng)比賽,囧,于是就這樣被被動(dòng)放了鴿子。好在后來(lái)遇到了同樣被霸哥(韓飛)當(dāng)導(dǎo)師而放鴿子的川總(杜川),川總對(duì)于 Streaming SQL 非常感興趣,于是難兄難弟一拍即合,迅速?zèng)Q定抱團(tuán)取暖。隨后,Robot 又介紹了同樣還沒(méi)有組隊(duì)的社區(qū)小伙伴 GZY(高志遠(yuǎn)),這樣算是湊齊了三個(gè)人,但是一想到?jīng)]有前端肯定搞不定,于是就拜托娘家人(Dashbase)的交際小王子 WPH(王鵬翰)出馬,幫助去召喚一個(gè)靠譜的前端小伙伴,后來(lái)交際未果直接把自己賣進(jìn)了隊(duì)伍,這樣終于湊齊了四后端,不,應(yīng)該是三后端 + 一偽前端的組合。
因?yàn)轳R上要準(zhǔn)備提交項(xiàng)目和團(tuán)隊(duì)名稱,大家都一致覺(jué)得方案二非常有意思,所以就選定了更加儒雅的 TBSSQL(TiDB Batch and Streaming SQL)作為項(xiàng)目名稱,TSBSQL 遺憾落選。在團(tuán)隊(duì)名稱方面,打醬油老男孩 / Scboy / TiStream / 養(yǎng)生 Hackathon / 佛系 Hackathon 都因?yàn)椴粔蚍蠚赓|(zhì)被遺憾淘汰,最后代表更有青春氣息的 TiBoys 入選(跟著我左手右手一個(gè)慢動(dòng)作,逃……
前期準(zhǔn)備所謂 “三軍未動(dòng), 糧草先行”,既然已經(jīng)報(bào)名了,還是要稍作準(zhǔn)備,雖然已經(jīng)確定了大的方向,但是具體的落地方案還沒(méi)有細(xì)化,而且人員的分工也不是太明確。又經(jīng)過(guò)一輪簡(jiǎn)單的討論之后,明確了大家的職責(zé)方向,我這邊主要負(fù)責(zé)項(xiàng)目整體設(shè)計(jì),進(jìn)度管理以及和 TiDB 核心相關(guān)的代碼,川總主要負(fù)責(zé) TiDB 核心技術(shù)攻關(guān),GZY 負(fù)責(zé)流數(shù)據(jù)源數(shù)據(jù)的采集部分,WPH 負(fù)責(zé)前端展現(xiàn)以及 Hackathon 當(dāng)天的 Demo 演示,分工之后大家就開(kāi)始分頭調(diào)研動(dòng)工。
作為這兩年來(lái)基本沒(méi)怎么寫過(guò)代碼的退役型選手來(lái)說(shuō),心里還是非常沒(méi)底的,也不知道現(xiàn)在 TiDB 代碼結(jié)構(gòu)和細(xì)節(jié)變成什么樣了,不求有功,但求別太拖后腿。
對(duì)于項(xiàng)目本身的典型應(yīng)用場(chǎng)景,大家還是比較明確的,覺(jué)得這個(gè)方向是非常有意義的。
應(yīng)用層系統(tǒng):實(shí)時(shí)流事件和離線數(shù)據(jù)的關(guān)聯(lián)查詢,比如在線廣告推薦系統(tǒng),在線推薦系統(tǒng),在線搜索,以及實(shí)時(shí)反欺詐系統(tǒng)等。
內(nèi)部數(shù)據(jù)系統(tǒng):
實(shí)時(shí)數(shù)據(jù)采樣統(tǒng)計(jì),比如內(nèi)部監(jiān)控系統(tǒng);
時(shí)間窗口數(shù)據(jù)分析系統(tǒng),比如實(shí)時(shí)的數(shù)據(jù)流數(shù)據(jù)分析(分析一段時(shí)間內(nèi)異常的數(shù)據(jù)流量和系統(tǒng)指標(biāo)),用于輔助做 AI Ops 相關(guān)的事情(比如根據(jù)數(shù)據(jù)流量做節(jié)點(diǎn)自動(dòng)擴(kuò)容/自動(dòng)提供參數(shù)調(diào)優(yōu)/異常流量和風(fēng)險(xiǎn)報(bào)告等等)。
業(yè)界 Streaming 相關(guān)的系統(tǒng)很多,前期我這邊快速地看了下能不能站在巨人的肩膀上做事情,有沒(méi)有可借鑒或者可借用的開(kāi)源項(xiàng)目。
Apache Beam
本質(zhì)上 Apache Beam 還是一個(gè)批處理和流處理融合的 SDK Model,用戶可以在應(yīng)用層使用更簡(jiǎn)單通用的函數(shù)接口實(shí)現(xiàn)業(yè)務(wù)的處理,如果使用 Beam 的話,還需要實(shí)現(xiàn)自定義的 Runner,因?yàn)?TiDB 本身主要的架構(gòu)設(shè)計(jì)非常偏重于數(shù)據(jù)庫(kù)方向,內(nèi)部并沒(méi)有特別明確的通用型計(jì)算引擎,所以現(xiàn)階段基本上沒(méi)有太大的可行性。當(dāng)然也可以選擇用 Flink 作為 Runner 連接 TiDB 數(shù)據(jù)源,但是這就變成了 Flink&TiDB 的事情了,和 Beam 本身關(guān)系其實(shí)就不大了。
Apache Flink / Spark Streaming
Flink 是一個(gè)典型的流處理系統(tǒng),批處理可以用流處理來(lái)模擬出來(lái)。
本身 Flink 也是支持 SQL 的,但是是一種嵌入式 SQL,也就是 SQL 和應(yīng)用程序代碼寫在一起,這種做法的好處是可以直接和應(yīng)用層進(jìn)行整合,但是不好的地方在于,接口不是太清晰,有業(yè)務(wù)侵入性。阿里內(nèi)部有一個(gè)增強(qiáng)版的 Flink 項(xiàng)目叫 Blink,在這個(gè)領(lǐng)域比較活躍。如果要實(shí)現(xiàn)批處理和流處理融合的話,需要內(nèi)部定制和修改 Flink 的代碼,把 TiDB 作為數(shù)據(jù)源對(duì)接起來(lái),還有可能需要把一些環(huán)境信息提交給 TiDB 以便得到更好的查詢結(jié)果,當(dāng)然或許像 TiSpark 那樣,直接 Flink 對(duì)接 TiKV 的數(shù)據(jù)源應(yīng)該也是可以的。因?yàn)楸旧韴F(tuán)隊(duì)對(duì)于 Scala/Java 代碼不是很熟悉,而且 Flink 的模式會(huì)有一定的侵入性,所以就沒(méi)有在這方面進(jìn)行更多的探索。同理,沒(méi)有選擇 Spark Streaming 也是類似的原因。當(dāng)然有興趣的小伙伴可以嘗試下這個(gè)方向,也是非常有意思的。
Kafka SQL
因?yàn)?Kafka 本身只是一個(gè) MQ,以后會(huì)向著流處理方向演進(jìn),但是目前并沒(méi)有實(shí)現(xiàn)批處理和流處理統(tǒng)一的潛力,所以更多的我們只是借鑒 Kafka SQL 的語(yǔ)法。目前 Streaming SQL 還沒(méi)有一個(gè)統(tǒng)一的標(biāo)準(zhǔn) SQL,Kafka SQL 也只是一個(gè) SQL 方言,支持的語(yǔ)法還比較簡(jiǎn)單,但是非常實(shí)用,而且是偏交互式的,沒(méi)有業(yè)務(wù)侵入性。非常適合在 Hackathon 上做 Demo 演示,我們?cè)陧?xiàng)目實(shí)現(xiàn)中也是主要參考了 Kafka SQL 的定義,當(dāng)然,F(xiàn)link 和 Calcite 也有自己定義的 Streaming 語(yǔ)法,這里就不再討論了。
調(diào)研準(zhǔn)備工作討論到這里基本上也就差不多了,于是我們開(kāi)始各自備(hua)戰(zhàn)(shui),出差的出差,加班的加班,接客戶的接客戶,學(xué) Golang 的學(xué) Golang,在這種緊(fang)張(fei)無(wú)(zi)比(wo)的節(jié)奏中,迎來(lái)了 Hackathon 比賽的到來(lái)。
Hackathon 流水賬具體的技術(shù)實(shí)現(xiàn)方面都是比較硬核的東西,細(xì)節(jié)也比較多,扔在最后面寫,免的大家看到一半就點(diǎn)×了。Day 1 3:30 AM至于參加 Hackathon 的感受,因?yàn)椴幌颀埜缒敲次暮?,也不像馬老師那么俏皮,而且本來(lái)讀書也不多,所以也只能喊一句“黑客馬拉松真是太好玩了”!
由于飛機(jī)晚點(diǎn),川總這個(gè)點(diǎn)兒才輾轉(zhuǎn)到酒店。睡覺(jué)之前非常擔(dān)心一覺(jué)睡過(guò)頭,讓這趟 Hackathon 之旅還沒(méi)開(kāi)始就結(jié)束了,沒(méi)想到躺下以后滿腦子都是技術(shù)細(xì)節(jié),怎么都睡不著。漫漫長(zhǎng)夜,無(wú)眠。
7:45 AM川總早早來(lái)到 Hackathon 現(xiàn)場(chǎng)。由于來(lái)太早,其他選手都還沒(méi)到,所以他提前刺探刺探敵情的計(jì)劃也泡湯了,只好在賽場(chǎng)瞎晃悠一番熟悉熟悉環(huán)境,順道跟大獎(jiǎng)合了個(gè)影。
11:00 AM簡(jiǎn)單的開(kāi)幕式之后,Hackathon 正式開(kāi)始。我們首先搞定的是 Streaming SQL 的語(yǔ)法定義以及 Parser 相關(guān)改動(dòng)。這一部分在之前就經(jīng)過(guò)比較詳細(xì)的在線討論了,所以現(xiàn)場(chǎng)只需要根據(jù)碰頭后統(tǒng)一的想法一頓敲敲敲就搞定了。快速搞定這一塊以后,我們就有了 SQL 語(yǔ)法層面的 Streaming 實(shí)現(xiàn)。當(dāng)然此時(shí) Streaming 也僅限于語(yǔ)法層面,Streaming 在 SQL 引擎層面對(duì)應(yīng)的其實(shí)還是普通的TiDB Table。
接下來(lái)是 DDL 部分。這一塊我們已經(jīng)想好了要復(fù)用 TiDB Table 的 Meta 結(jié)構(gòu) TableInfo ,因此主要工作就是按照 DDL源碼解析 依葫蘆畫瓢,難度也不大,以至于我們還有閑心糾結(jié)一下 SHOW TABLES 語(yǔ)法里到底要不要屏蔽掉 Streaming Table 的問(wèn)題。
整體上來(lái)看上午的熱身活動(dòng)還是進(jìn)行的比較順利的,起碼 Streaming DDL 這塊沒(méi)有成為太大的問(wèn)題。這里面有個(gè)插曲就是我在 Hackathon 之前下載編譯 TiDB,結(jié)果發(fā)現(xiàn) TiDB 的 parser 已經(jīng)用上時(shí)髦的 go module 了(也是好久好久沒(méi)看 TiDB 代碼),折騰好半天,不過(guò)好處就是 Hackathon 當(dāng)天的時(shí)候改起來(lái) parser 就比較輕車熟路了,所以賽前編譯一個(gè) TiDB 還是非常有必要的。
15:30 PM隨著熱身的結(jié)束,馬上迎來(lái)了穩(wěn)定的敲敲敲階段。川總簡(jiǎn)單弄了一個(gè) Mock 的 StreamReader 然后丟給了我,因?yàn)槲抑皩?TiDB 的時(shí)候,時(shí)代比較遙遠(yuǎn),那時(shí)候都還在用周 sir 的 Datum,現(xiàn)在一看,為了提高內(nèi)存效率和性能,已經(jīng)換成了高大上的 Chunk,于是一個(gè)很常見(jiàn)的問(wèn)題:如何用最正確的做法把一個(gè)傳過(guò)來(lái)的 Json 數(shù)據(jù)格式化成 Table Row 數(shù)據(jù)放到 Chunk 里面,讓徹底我懵逼了。
這里面倒不是技術(shù)的問(wèn)題,主要是類型太多,如果枚舉所有類型,搞起來(lái)很麻煩,按道理應(yīng)該有更輕快的辦法,但是翻了源代碼還是沒(méi)找到解決方案。這個(gè)時(shí)候果斷去求助現(xiàn)場(chǎng)導(dǎo)師,也順便去賽場(chǎng)溜(ci)達(dá)(tan)一(di)圈(qing)。隨便掃了一眼,驚呆了,龍哥他們竟然已經(jīng)開(kāi)始寫 PPT 了,之前知道龍哥他們強(qiáng),但是沒(méi)想到強(qiáng)到這個(gè)地步,還讓不讓大家一塊歡快地玩耍了。同時(shí),也了解到了不少非常有意思的項(xiàng)目,比如用機(jī)器學(xué)習(xí)方法去自動(dòng)調(diào)節(jié) TiDB 的調(diào)度參數(shù),用 Lua 給 TiKV 添加 UDF 之類的,在 TiDB 上面實(shí)現(xiàn)異構(gòu)數(shù)據(jù)庫(kù)的關(guān)聯(lián)查詢(簡(jiǎn)直就是 F1 的大一統(tǒng),而且聽(tīng)小道消息,他們都已經(jīng)把 Join 推到 PG 上面去了,然而我們還沒(méi)開(kāi)始進(jìn)入到核心開(kāi)發(fā)流程),在 TiKV 上面實(shí)現(xiàn)時(shí)序數(shù)據(jù)庫(kù)和 Memcached 協(xié)議等等,甚至東旭都按捺不住自己 Hackathon 起來(lái)了(嘻嘻,可以學(xué)學(xué)我啊 ;D )。
本來(lái)還想去聊聊各個(gè)項(xiàng)目的具體實(shí)現(xiàn)方案,但是一想到自己挖了一堆坑還沒(méi)填,只能默默回去膜拜 TiNiuB 項(xiàng)目??雌饋?lái)不能太佛系了,于是乎我趕緊召開(kāi)了一次內(nèi)部團(tuán)隊(duì) sync 的 catch up,明確下分工,川總開(kāi)始死磕 TBSSQL 的核心邏輯 Streaming Aggregation 的實(shí)現(xiàn),我這邊繼續(xù)搞不帶 Aggregation 的 Streaming SQL 的其他實(shí)現(xiàn),GZY 已經(jīng)部署起來(lái)了 Pulsar,開(kāi)始準(zhǔn)備 Mock 數(shù)據(jù),WPH 輔助 GZY 同時(shí)也快速理解我們的 Demo 場(chǎng)景,著手設(shè)計(jì)實(shí)現(xiàn)前端展現(xiàn)。
18:00 PM我這邊和面帶慈父般欣慰笑容的老師(張建)進(jìn)行了一些技術(shù)方案實(shí)現(xiàn)上的交流后,了解到目前社區(qū)小伙伴已經(jīng)在搞 CREATE TABLE AS SELECT 的重要信息(后續(xù)證明此信息值大概一千塊 RMB)。
此時(shí),在解決了之前的問(wèn)題之后,TBSSQL 終于能跑通簡(jiǎn)單的 SELECT 語(yǔ)句了。我們心里稍微有點(diǎn)底了,于是一鼓作氣,順路也實(shí)現(xiàn)了帶 Where 條件的 Stream Table 的 SELECT,以及 Stream Table 和 TiDB Table 的多表 Join,到這里,此時(shí),按照分工,我這邊的主體工作除了 Streaming Position 的持久化支持以外,已經(jīng)寫的差不多了,剩下就是去實(shí)現(xiàn)一些 Nice to have 的 DDL 的語(yǔ)法支持。川總這里首先要搞的是基于時(shí)間窗口的 Streaming Aggregation。按照我們的如意算盤,這里基本上可以復(fù)用 TiDB 現(xiàn)有的 Hash Aggregation 的計(jì)算邏輯,只需要加上窗口的處理就完事兒了。
不過(guò)實(shí)際下手的時(shí)候仔細(xì)一研究代碼,發(fā)現(xiàn) Aggregation 這一塊代碼在川總疏于研究這一段時(shí)間已經(jīng)被重構(gòu)了一把,加上了一個(gè)并發(fā)執(zhí)行的分支,看起來(lái)還挺復(fù)雜。于是一不做二不休,川總把 Hash Aggregation 的代碼拷了一份,刪除了并發(fā)執(zhí)行的邏輯,在比較簡(jiǎn)單的非并發(fā)分支加上窗口相關(guān)實(shí)現(xiàn)。不過(guò)這種方法意味著帶時(shí)間窗口的 Aggregation 得多帶帶出 Plan,Planner 上又得改一大圈。這一塊弄完以后,還沒(méi)來(lái)得及調(diào)試,就到吃晚飯的點(diǎn)兒了。
21:00 PM吃完晚飯,因?yàn)橄挛缢揽牡谋容^厲害,我和張建、川總出門去園區(qū)溜達(dá)了一圈。期間張建問(wèn)我們搞得咋樣了,我望了一眼川總,語(yǔ)重心長(zhǎng)地說(shuō)主要成敗已經(jīng)不在我了(后續(xù)證明這句語(yǔ)重心長(zhǎng)至少也得值一千塊 RMB),川總果斷信心滿滿地說(shuō)問(wèn)題不大,一切盡在掌握之中。
沒(méi)想到這個(gè) Flag 剛立起來(lái)還是溫的,就立馬被打臉了。問(wèn)題出在吃飯前搞的聚合那塊(具體細(xì)節(jié)可以看下后面的坑系列),為了支持時(shí)間窗口,我們必須確保 Streaming 上的窗口列能透?jìng)鞯骄酆纤阕赢?dāng)中,為此我們屏蔽了優(yōu)化器中窗口聚合上的列裁剪規(guī)則。可是實(shí)際運(yùn)行當(dāng)中,我們的修改并沒(méi)有生效???而此時(shí),川總昨天一整晚沒(méi)睡覺(jué)的副作用開(kāi)始顯現(xiàn)出來(lái)了,思路已經(jīng)有點(diǎn)不太清醒了。于是我們把張建拖過(guò)來(lái)一起 debug。然后我這邊也把用 TiDB Global Variable 控制 Streaming Position 的功能實(shí)現(xiàn)了,并且和 GZY 這邊也實(shí)現(xiàn)了 Mock 數(shù)據(jù)。
之后,我也順路休息休息,畢竟川總這邊搞不定,我們這邊搞的再好也沒(méi)啥用。除了觀摩川總和張建手把手,不,肩并肩結(jié)對(duì)小黑屋編程之外,我也順便申請(qǐng)了部署 Kafka 聯(lián)調(diào)的機(jī)器。
23:00 PM我們這邊最核心的功能還沒(méi)突破,亮眼的 CREATE TABLE AS SELECT Streaming 也還沒(méi)影,其實(shí)中期進(jìn)度還是偏慢了(或者說(shuō)之前我設(shè)計(jì)實(shí)現(xiàn)的功能的工作量太大了,看起來(lái)今天晚上只能死磕了,囧)。我調(diào)試 Kafka 死活調(diào)不通,端口可以 Telnet 登陸,但是寫入和獲取數(shù)據(jù)的時(shí)候一直報(bào)超時(shí)錯(cuò)誤,而且我這邊已經(jīng)開(kāi)始困上來(lái)了,有點(diǎn)扛不動(dòng)了,后來(lái)在 Kafka 老司機(jī) WPH 一起看了下配置參數(shù),才發(fā)現(xiàn) Advertise URL 設(shè)置成了本地地址,換成對(duì)外的 IP 就好了,當(dāng)然為了簡(jiǎn)單方便,我們?cè)O(shè)置了單 Partition 的 Topic,這樣 collector 的 Kafka 部分就搞的差不多了,剩下就是實(shí)現(xiàn)一個(gè) http 的 restful api 來(lái)提供給 TiDB 的 StreamReader 讀取,整個(gè)連通工作就差不多了。
Day 2 00:00 AM這時(shí)候川總那邊也傳來(lái)了好消息,終于從 Streaming Aggregation 這個(gè)大坑里面爬出來(lái)了,后面也比較順利地搞定了時(shí)間窗口上的聚合這塊。此時(shí)時(shí)間已經(jīng)到了 Hackathon 的第二天,不少其他項(xiàng)目的小伙伴已經(jīng)收攤回家了。不過(guò)我們抱著能多做一個(gè) Feature 是一個(gè)的心態(tài),決定挑燈夜戰(zhàn)。首先,川總把 Sort Executor 改了一把以支持時(shí)間窗口,可能剛剛的踩坑經(jīng)歷為我們攢了人品,Sort 上的改動(dòng)竟然一次 AC 了。借著這股勁兒,我們又回頭優(yōu)化了一把 SHOW CREATE STREAM 的輸出。
這里有個(gè)插曲就是為了近距離再回味和感受下之前的開(kāi)發(fā)流程,我們特意在 TiDB 的 repo 里面開(kāi)了一個(gè) tiboys/hackathon 的分支,然后提交的時(shí)候用了標(biāo)準(zhǔn)的 Pull Request 的方式,點(diǎn)贊了才能 merge(后來(lái)想想打 Hackathon 不是太可取,沒(méi)什么用,還挺耽誤時(shí)間,不知道當(dāng)時(shí)怎么想的),所以在 master 分支和 tiboys/hackathon 分支看的時(shí)候都沒(méi)有任何提交記錄。嘻嘻,估計(jì)龍哥也沒(méi)仔細(xì)看我們的 repo,所以其實(shí)在龍哥的激勵(lì)下,我們的效率還是可以的 :) 。
2:30 AMGZY 和 WPH 把今天安排的工作完成的差不多了,而且第二天還靠他們主要準(zhǔn)備 Demo Show,就去睡覺(jué)了,川總也已經(jīng)困得不行了,準(zhǔn)備打烊睡覺(jué)。我和川總合計(jì)了一下,還差一個(gè)最重要的 Feature,抱著就試一把,不行就手工的心態(tài),我們把社區(qū)的小伙伴王聰(bb7133)提的支持 CREATE TABLE AS SELECT 語(yǔ)法的 PR 合到了我們的分支,沖突竟然不是太多,然后稍微改了一下來(lái)支持 Streaming,結(jié)果一運(yùn)行奇跡般地發(fā)現(xiàn)竟然能夠運(yùn)行,RP 全面爆發(fā)了,于是我們就近乎免費(fèi)地增加了一個(gè) Feature。改完這個(gè)地方,川總實(shí)在堅(jiān)持不住了,就回去睡了。我這邊的 http restful api 也搞的差不多了,準(zhǔn)備聯(lián)調(diào)一把,StreamReader 通過(guò) http client 從 collector 讀數(shù)據(jù),collector 通過(guò) kafka consumer 從 kafka broker 獲取數(shù)據(jù),結(jié)果獲取的 Json 數(shù)據(jù)序列化成 TiDB 自定義的 Time 類型老是出問(wèn)題,于是我又花了一些時(shí)間給 Time 增加了 Marshall 和 Unmarshal 的格式化支持,到這里基本上可以 work 了,看了看時(shí)間,凌晨四點(diǎn)半,我也準(zhǔn)備去睡了。期間好幾次看到霸哥(韓飛)凌晨還在一直幫?。╰ian)伙(zi)伴(ji)查(wa)問(wèn)(de)題(keng),其實(shí)霸哥認(rèn)真的時(shí)候還是非??孔V的。
7:30 AM這個(gè)時(shí)候人陸陸續(xù)續(xù)地來(lái)了,我這邊也進(jìn)入了打醬油的角色,年紀(jì)大了確實(shí)剛不動(dòng)了,吃了早餐之后,開(kāi)始準(zhǔn)備思考接下來(lái)的分工。因?yàn)榇蠹叶际桥R時(shí)組隊(duì),到了 Hackathon 才碰面,基本上沒(méi)有太多磨合,而且普遍第二天狀態(tài)都不大好。雖然大家都很努力,但是在我之前設(shè)計(jì)的宏大項(xiàng)目面前,還是感覺(jué)人力不太夠,所以早上 10 點(diǎn)我們開(kāi)了第二次 sync 的 catch up,討論接下來(lái)的安排。我去負(fù)責(zé)更新代碼和 GitHub 的 Readme,川總最后再簡(jiǎn)單對(duì)代碼掃尾,順便和 GZY 去錄屏(羅伯特小姐姐介紹的不翻車經(jīng)驗(yàn)),WPH 準(zhǔn)備畫圖和 PPT,因?yàn)闀r(shí)間有限,前端展現(xiàn)部分打算從賣家秀直接轉(zhuǎn)到買家秀。11 點(diǎn)敲定代碼完全封板,然后安心準(zhǔn)備 PPT 和下午的 Demo。
14:00 PM因?yàn)槌楹灣榈谋容^靠后,主要事情在 WPH 這邊,我和川總基本上也沒(méi)什么大事了,順手搞了幾幅圖,然后跟馬老師還有其他項(xiàng)目的小伙伴們開(kāi)始八卦聊天。因?yàn)檎弥苣依锩米淤I東西順便過(guò)來(lái)慰問(wèn)了下。下午主要聽(tīng)了各個(gè) Team 的介紹,欣賞到了極盡浮夸的 LOGO 動(dòng)畫,Get 到了有困難找 Big Brother 的新技能,學(xué)習(xí)和了解了很有意思的 Idea,真心覺(jué)得這屆 Hackathon 做的非常值得回憶。
從最后的現(xiàn)場(chǎng)展示情況來(lái)看,因?yàn)?TBSSQL 內(nèi)容比較多,真的展示下來(lái),感覺(jué) 6 分鐘時(shí)間還是太趕,好在 WPH Demo 的還是非常順利的,把我們做的事情都展示出來(lái)了。因?yàn)榭车袅艘恍┣岸苏宫F(xiàn)的部分(這塊我們也確實(shí)不怎么擅長(zhǎng)),其實(shí)對(duì)于 Hackathon 項(xiàng)目是非常吃虧的,不過(guò)有一點(diǎn)比較欣慰,就像某光頭大佬說(shuō)的,評(píng)委們都是懂技術(shù)的。因?yàn)閷?shí)現(xiàn)完整性方面能做的也都搞差不多了,打的雖然很累但是也很開(kāi)心,對(duì)于結(jié)果也就不怎么糾結(jié)了。
因?yàn)榇偼砩系娘w機(jī),小伙伴們簡(jiǎn)單溝通了幾句,一致同意去園區(qū)找個(gè)地吃個(gè)晚飯,于是大家拉上霸哥去了“頭一號(hào)”,也是第一次吃了大油條,中間小伙伴們各種黑誰(shuí)誰(shuí)誰(shuí)寫的 bug 巴拉巴拉的,后來(lái)看手機(jī)群里有人 @ 我說(shuō)拿獎(jiǎng)了。
其實(shí)很多項(xiàng)目各方面綜合實(shí)力都不錯(cuò),可以說(shuō)是各有特色,很難說(shuō)的上哪個(gè)項(xiàng)目有絕對(duì)的優(yōu)勢(shì)。我們之前有討論過(guò),TBSSQL 有獲獎(jiǎng)的贏面,畢竟從完整性,實(shí)用性和生態(tài)方面都是有潛質(zhì)的,但是能獲得大家最高的認(rèn)可還是小意外的,特別感謝各位技術(shù)大佬們,也特別感謝幫助我們領(lǐng)獎(jiǎng)的滿分羅伯特小姐姐。
最后大家補(bǔ)了一張合照,算是為這次 Hackathon 畫下一個(gè)句號(hào)。
至此,基本上 Hackathon 的流水賬就記錄完了,整個(gè)項(xiàng)目地址在 https://github.com/qiuyesuifeng/tidb 歡迎大家關(guān)注和討論。
選讀:技術(shù)實(shí)現(xiàn)TLDR: 文章很長(zhǎng),挑感興趣的部分看看就可以了。
在前期分析和準(zhǔn)備之后,基本上就只有在 TiDB 上做 SQL Streaming 引擎一條路可選了,細(xì)化了下要實(shí)現(xiàn)的功能以及簡(jiǎn)單的系統(tǒng)架構(gòu),感覺(jué)工作量還是非常大的。
下面簡(jiǎn)單介紹下系統(tǒng)架構(gòu)和各個(gè)模塊的功能:
在數(shù)據(jù)源采集部分(collector),我們計(jì)劃選取幾種典型的數(shù)據(jù)源作為適配支持。
Kafka
最流行的開(kāi)源 MQ 系統(tǒng),很多 Streaming 系統(tǒng)對(duì)接的都是 Kafka。
Pulsar
流行的開(kāi)源 MQ 系統(tǒng),目前比較火爆,有趕超 Kafka 的勢(shì)頭。
Binlog
支持 MySQL/TiDB Binlog 處理,相當(dāng)于是 MySQL Trigger 功能的升級(jí)加強(qiáng)版了。我們對(duì)之前的 MySQL -> TiDB 的數(shù)據(jù)同步工具 Syncer 也比較熟悉,所以這塊工作量應(yīng)該也不大。
Log
常見(jiàn)的 Log 日志,這個(gè)就沒(méi)什么好解釋的了。
為了方便 Demo 和協(xié)作,collector 除了適配不同的數(shù)據(jù)源,還會(huì)提供一個(gè) restful api 的接口,這樣 TBSSQL 就可以通過(guò) pull 的方式一直獲取 streaming 的數(shù)據(jù)。因?yàn)?collector 主要是具體的工程實(shí)現(xiàn),所以就不在這里細(xì)節(jié)展開(kāi)了,感興趣的話,可以參考下 相關(guān)代碼。
要在 TiDB 中實(shí)現(xiàn) Streaming 的功能即 TBSSQL,就需要在 TiDB 內(nèi)部深入定制和修改 TiDB 的核心代碼。
Streaming 有兩個(gè)比較本質(zhì)的特征:
Streaming 具有流式特性,也就是說(shuō),其數(shù)據(jù)可以是一直增長(zhǎng),無(wú)窮無(wú)盡的。而在 Batch 系統(tǒng)(暫時(shí)把 MySQL/TIDB 這種數(shù)據(jù)在一定時(shí)間內(nèi)相對(duì)穩(wěn)定的系統(tǒng)簡(jiǎn)稱 Batch 系統(tǒng),下面都會(huì)沿用這種說(shuō)法)當(dāng)中,每個(gè) SQL 的輸入數(shù)據(jù)集是固定,靜態(tài)的。
Streaming 具有時(shí)序特性。每一條數(shù)據(jù)都有其內(nèi)在的時(shí)間屬性(比如說(shuō)事件發(fā)生時(shí)間等),數(shù)據(jù)之間有先后順序關(guān)系。而在 Batch 系統(tǒng)當(dāng)中,一個(gè)表中的數(shù)據(jù)在時(shí)間維度上是無(wú)序的。
因此,要在 TiDB SQL 引擎上支持 Streaming SQL,所涉及到的算子都需要根據(jù) Streaming 的這兩個(gè)特點(diǎn)做修改。以聚合函數(shù)(Aggregation)為例,按照 SQL 語(yǔ)義,聚合算子的實(shí)現(xiàn)應(yīng)該分成兩步:首先是 Grouping, 即對(duì)輸入按照聚合列進(jìn)行分組;然后是 Execute, 即在各個(gè)分組上應(yīng)用聚合函數(shù)進(jìn)行計(jì)算,如下圖所示。
對(duì)于 Streaming,因?yàn)槠漭斎肟梢允菬o(wú)盡的,Grouping 這個(gè)階段永遠(yuǎn)不可能結(jié)束,所以按照老套路,聚合計(jì)算就沒(méi)法做了。這時(shí),就要根據(jù) Streaming 的時(shí)序特性對(duì) Streaming 數(shù)據(jù)進(jìn)行分組。每一個(gè)分組被稱為一個(gè) Time Window(時(shí)間窗口)。就拿最簡(jiǎn)單的 Tumbling Window 來(lái)說(shuō),可以按照固定的時(shí)間間隔把 Streaming 輸入切分成一個(gè)個(gè)相互無(wú)交集的窗口,然后在每一個(gè)窗口上就可以按照之前的方式進(jìn)行聚合了。
聚合算子只是一個(gè)比較簡(jiǎn)單的例子,因?yàn)槠渲簧婕耙宦份斎?。如果要修改多路輸入的算子(比如說(shuō) Join 多個(gè) Streaming),改動(dòng)更復(fù)雜。此外,時(shí)間窗口的類型也是多種多樣,剛剛例子中的 Tumbling Window 只是基礎(chǔ)款,還有復(fù)雜一點(diǎn)的 Hopping Window 以及更復(fù)雜的 Sliding Window。在 Hackathon 的有限時(shí)間內(nèi),我們既要考慮實(shí)現(xiàn)難度,又要突出 Batch / Streaming 融合處理的特點(diǎn),因此在技術(shù)上我們做出如下抉擇:
時(shí)間窗口只做最基本的 Tumbling Window。
實(shí)現(xiàn)基于時(shí)間窗口的 Aggregation 和 Sort 作為經(jīng)典流式算子的代表。
實(shí)現(xiàn)單 Streaming Join 多 Batch Table 作為 Batch / Streaming 融合的示例, 多個(gè) Streaming Join 太復(fù)雜,因?yàn)闀r(shí)間有限就先不做了。
支持 Streaming 處理結(jié)果寫入 Batch Table(TiDB Table)這種常見(jiàn)但是非常實(shí)用的功能。也就是說(shuō)要支持 CREATE TABLE AS SELECT xxx FROM streaming 的類似語(yǔ)法。
此外,既然是要支持 Streaming SQL,選擇合適的 SQL 語(yǔ)法也是必要的,需要在 Parser 和 DDL 部分做相應(yīng)的修改。單整理下,我們的 Feature List 如下圖所示:
下面具體聊聊我們實(shí)現(xiàn)方案中的一些關(guān)鍵選擇。
Streaming SQL 語(yǔ)法
Streaming SQL 語(yǔ)法的核心是時(shí)間窗口的定義,Time Window 和一般 SQL 中的 Window Function 其實(shí)語(yǔ)義上是有區(qū)別的。在 Streaming SQL 中,Time Window 主要作用是為后續(xù)的 SQL 算子限定輸入的范圍,而在一般的 SQL 中,Window Funtion 本身就是一個(gè) SQL 算子,里面的 Window 其實(shí)起到一個(gè) Partition 的作用。
在純 Streaming 系統(tǒng)當(dāng)中,這種語(yǔ)義的差別影響不大,反而還會(huì)因?yàn)檎Z(yǔ)法的一致性降低用戶的學(xué)習(xí)成本,但是在 TBSSQL 這種 Batch / Streaming 混合場(chǎng)景下,同一套語(yǔ)法支持兩種語(yǔ)義,會(huì)對(duì)用戶的使用造成一定困擾,特別是在 TiDB 已經(jīng)被眾多用戶應(yīng)用到生產(chǎn)環(huán)境這種背景下,這種語(yǔ)義上的差別一定要體現(xiàn)在語(yǔ)法的差異上。
Sreaming DDL
DDL 這一塊實(shí)現(xiàn)難度不大,只要照著 DDL源碼解析 依葫蘆畫瓢就行。這里值得一提的是在 Meta 層,我們直接(偷懶)復(fù)用了 TableInfo 結(jié)構(gòu)(加了判斷是否為 Streaming 的 Flag 和一些表示 Streaming 屬性的字段)來(lái)表示 Streaming Table。這個(gè)選擇主要是從實(shí)現(xiàn)難度上考慮的,畢竟復(fù)用現(xiàn)有的結(jié)構(gòu)是最快最安全的。但是從設(shè)計(jì)思想上看,這個(gè)決定其實(shí)也暗示了在 TBSSQL 當(dāng)中,Streaming 是 Table 的一種特殊形式,而不是一個(gè)獨(dú)立的概念。理解這一點(diǎn)很重要,因?yàn)檫@是一些其他設(shè)計(jì)的依據(jù)。比如按照以上設(shè)定,那么從語(yǔ)義上講,在同一個(gè) DB 下 Streaming 和普通 Table 就不能重名,反之的話這種重名就是可以接受的。
StreamReader
這一塊主要有兩個(gè)部分,一個(gè)是適配不同的數(shù)據(jù)源(collector),另一個(gè)是將 Streaming 數(shù)據(jù)源引入 TiDB 計(jì)算引擎(StreamReader)。collector 這部分上面已經(jīng)介紹過(guò)了,這里就不再過(guò)多介紹了。StreamReader 這一塊,主要要修改由 LogicalPlan 生成 PhysicalPlan(具體代碼),以及由 PhysicalPlan 生成 Executor Operator Tree 的過(guò)程(具體代碼)。StreamReader 的 Open 方法中,會(huì)利用 Meta 中的各種元信息來(lái)初始化與 collector 之間的連接,然后在 Next 方法中通過(guò) Pull 的方式不斷拉取數(shù)據(jù)。
對(duì)時(shí)間窗口的處理
前面我們提到,時(shí)間窗口是 Streaming 系統(tǒng)中的核心概念。那么這里就有一個(gè)重要的問(wèn)題,Time Window 中的 Time 如何界定?如何判斷什么時(shí)候應(yīng)該切換 Window?最容易想到,也是最簡(jiǎn)單粗暴的方式,就是按照系統(tǒng)的當(dāng)前時(shí)間來(lái)進(jìn)行切割。這種方式問(wèn)題很大,因?yàn)椋?/p>
數(shù)據(jù)從生成到被 TBSSQL 系統(tǒng)接收到,肯定會(huì)有一定的延遲,而且這個(gè)延遲時(shí)間是沒(méi)有辦法精確預(yù)估的。因此在用戶實(shí)際場(chǎng)景中,除非是要測(cè)量收發(fā)延遲,這個(gè)系統(tǒng)時(shí)間對(duì)用戶沒(méi)有太大意義。
考慮到算子并發(fā)執(zhí)行的可能性(雖然還沒(méi)有實(shí)現(xiàn)),不同機(jī)器的系統(tǒng)時(shí)間可能會(huì)有些許偏差,這個(gè)偏差對(duì)于 Window 操作來(lái)說(shuō)可能導(dǎo)致致命的誤差,也會(huì)導(dǎo)致結(jié)果的不精確(因?yàn)?Streaming 源的數(shù)據(jù) Shuffle 到不同的處理節(jié)點(diǎn)上,系統(tǒng)時(shí)間的誤差可能不太一樣,可能會(huì)導(dǎo)致 Window 劃分的不一樣)。
因此,比較合理的方式是以 Streaming 中的某一 Timestamp 類型的列來(lái)切分窗口,這個(gè)值由用戶在應(yīng)用層來(lái)指定。當(dāng)然 Streaming 的 Schema 中可能有多個(gè) Timestamp 列,這里可以要求用戶指定一個(gè)作為 Window 列。在實(shí)現(xiàn) Demo 的時(shí)候,為了省事,我們直接限定了用戶 Schema 中只能有一個(gè)時(shí)間列,并且以該列作為 Window 列([具體代碼](https://github.com/qiuyesuifeng/tidb/blob/656971da00a3b1f81f5085aaa277159868fca223/ddl/table.go#L58))。當(dāng)然這里帶來(lái)一個(gè)問(wèn)題,就是 Streaming 的 Schema 中必須有 Timestamp 列,不然這里就沒(méi)法玩了。為此,我們?cè)趧?chuàng)建 Streaming 的 DDL 中加了 [檢查邏輯](https://github.com/qiuyesuifeng/tidb/blob/656971da00a3b1f81f5085aaa277159868fca223/ddl/ddl_api.go#L149),強(qiáng)制 Streaming 的 Schema 必須有 Timestamp 列(其實(shí)我們也沒(méi)想明白當(dāng)初 Hackathon 為啥要寫的這么細(xì),這些細(xì)節(jié)為后來(lái)通宵埋下了濃重的伏筆,只能理解為程序猿的本能,希望這些代碼大家看的時(shí)候吐槽少一些)。
Streaming DML
這里簡(jiǎn)單 DML 指的就是不依賴時(shí)間窗口的 DML,比如說(shuō)只帶 Selection 和 Projection 的SELECT 語(yǔ)句,或者單個(gè) Streaming Join 多個(gè) Table。因?yàn)椴灰蕾嚂r(shí)間窗口,支持這類 DML 實(shí)際上不需要對(duì)計(jì)算層做任何改動(dòng),只要接入 Streaming 數(shù)據(jù)源就可以了。
對(duì)于 Streaming Join Table(如上圖表示的是 Stream Join User&Ads 表的示意圖) 可以多說(shuō)一點(diǎn),如果不帶 Time Window,其實(shí)這里需要修改一下Planner。因?yàn)?Streaming 的流式特性,這里可能沒(méi)法獲取其完整輸入集,因此就沒(méi)法對(duì) Streaming 的整個(gè)輸入進(jìn)行排序,所以 Merge Join 算法這里就沒(méi)法使用了。同理,也無(wú)法基于 Streaming 的整個(gè)輸入建 Hash 表,因此在 Hash Join 算法當(dāng)中也只能某個(gè)普通表 Build Hash Table。不過(guò),在我們的 Demo 階段,輸入其實(shí)也是還是有限的,所以這里其實(shí)沒(méi)有做,倒也影響不大。
基于時(shí)間窗口的 Aggregation 和 Sort
在 TBSSQL 當(dāng)中,我們實(shí)現(xiàn)了基于固定時(shí)間窗的 Hash Aggregation Operator 和 Sort Operator。這里比較正規(guī)的打法其實(shí)應(yīng)該是實(shí)現(xiàn)一個(gè)獨(dú)立的 TimeWindow,各種基于時(shí)間窗口的 Operator 可以切換時(shí)間窗的邏輯,然后比如 Aggregation 和 Sort 這類算子只關(guān)心自己的計(jì)算邏輯。 但是這樣一來(lái)要對(duì) Planner 做比較大的改動(dòng),想想看難度太大了,所以我們?cè)僖淮尾扇×酥保╰ou)接(lan)的方法,將時(shí)間窗口直接實(shí)現(xiàn)分別實(shí)現(xiàn)在 Aggregation 和 Sort 內(nèi)部,這樣 Planner 這塊不用做傷筋動(dòng)骨的改動(dòng),只要在各個(gè)分支邏輯上修修補(bǔ)補(bǔ)就可以了。
對(duì)于 Aggregation,我們還做了一些額外的修改。Aggregation 的輸出 Schema 語(yǔ)義上來(lái)說(shuō)只包括聚合列和聚合算子的輸出列。但是在引入時(shí)間窗口的情況下,為了區(qū)分不同的窗口的聚合輸出,我們?yōu)榫酆辖Y(jié)果顯式加上了兩個(gè) Timestamp 列 window_start 和 window_end, 來(lái)表示窗口的開(kāi)始時(shí)間和結(jié)束時(shí)間。為了這次這個(gè)小特性,我們踩到一個(gè)大坑,費(fèi)了不少勁,這個(gè)后面再仔細(xì)聊聊。
支持 Streaming 處理結(jié)果寫入 Batch Table
因?yàn)?TiDB 本身目前還暫時(shí)不支持 CREATE TABLE AS SELECT … 語(yǔ)法,而從頭開(kāi)始搞的話工作量又太大,因此我們一度打算放棄這個(gè) Feature。后面經(jīng)過(guò)老司機(jī)提醒,我們發(fā)現(xiàn)社區(qū)的小伙伴王聰(bb7133)已經(jīng)提了一個(gè) PR 在做這個(gè)事情了。本著試一把的想法我們把這個(gè) PR 合到我們的分支上一跑,結(jié)果竟然沒(méi)多少?zèng)_突,還真能 Work…...稍微有點(diǎn)問(wèn)題的是如果 SELECT 子句中有帶時(shí)間窗口的聚合,輸出的結(jié)果不太對(duì)。仔細(xì)研究了一下發(fā)現(xiàn),CREATE TABLE AS SELECT 語(yǔ)句中做 LogicalPlan 的路徑和直接執(zhí)行 SELECT 時(shí)做 LogicalPlan 的入口不太一致,以至于對(duì)于前者,我們做 LogicalPlan 的時(shí)候遺漏了一些 Streaming 相關(guān)信息。這里稍作修改以后,也能夠正常運(yùn)行了。
遇到的困難和坑本著前人采坑,后人盡量少踩的心態(tài)聊聊遇到的一些問(wèn)題,主要的技術(shù)方案上面已經(jīng)介紹的比較多了。限于篇幅,只描述遇到的最大的坑——消失的窗口列的故事。在做基于時(shí)間窗口的 Aggregation 的時(shí)候,我們要按照用戶指定的窗口列來(lái)切窗口。但是根據(jù) 列裁剪 規(guī)則,如果這個(gè)窗口列沒(méi)有被用作聚合列或者在聚合函數(shù)中被使用,那么這一列基本上會(huì)被優(yōu)化器裁掉。這里的修改很簡(jiǎn)單(我們以為),只需要在聚合的列裁剪邏輯中,如果發(fā)現(xiàn)聚合帶時(shí)間窗口,那么直接不做裁剪就完事兒了(代碼)。三下五除二修改完代碼,編譯完后一運(yùn)行,結(jié)果……瞬間 Panic 了……Debug 一看,發(fā)現(xiàn)剛剛的修改沒(méi)有生效,Streaming 的窗口列還是被裁剪掉了,隨后我們又把 Planner 的主要流程看了一遍,還是沒(méi)有在其他地方發(fā)現(xiàn)有類似的裁剪邏輯。
這時(shí)我們意識(shí)到事情沒(méi)有這么簡(jiǎn)單了,趕忙從導(dǎo)師團(tuán)搬來(lái)老司機(jī)(還是上面那位)。我們一起用簡(jiǎn)單粗暴的二分大法和 Print 大法,在生成 LogicalPlan,PhysicalPlan 和 Executor 前后將各個(gè)算子的 Schema 打印出來(lái)。結(jié)果發(fā)現(xiàn),在 PhysicalPlan 完成后,窗口列還是存在的,也就是說(shuō)我們的修改是生效了的,但是在生成 Executor 以后,這一列卻神秘消失了。所以一開(kāi)始我們定位的思路就錯(cuò)了,問(wèn)題出在生成 Executor 的過(guò)程,但是我們一直在 Planner 中定位,當(dāng)然找不到問(wèn)題。
明確了方向以后,我們很快就發(fā)現(xiàn)了元兇。在 Build HashAggregation 的時(shí)候,有一個(gè)不起眼的函數(shù)調(diào)用 buildProjBelowAgg,這個(gè)函數(shù)悄悄地在 Aggregation 算子下面加塞了一個(gè) Projection 算子,順道又做了一把列裁剪,最為頭疼的是,因?yàn)檫@個(gè) Projection 算子是在生成 Executor 階段才塞進(jìn)去的,而 EXPLAIN 語(yǔ)句是走不到這里來(lái)的,所以這個(gè) Projection 算子在做 Explain 的時(shí)候是看不見(jiàn)的,想當(dāng)于是一個(gè)隱形的算子,所以我們就這樣華麗麗地被坑了,于是就有了羅伯特小姐姐聽(tīng)到的那句 “xxx,出來(lái)挨打” 的橋段。
今后的計(jì)劃從立項(xiàng)之初,我們就期望 TBSSQL 能夠作為一個(gè)正式的 Feature 投入生產(chǎn)環(huán)境。為此,在設(shè)計(jì)和實(shí)現(xiàn)過(guò)程中,如果能用比較優(yōu)雅的解決方案,我們都盡量不 Hack。但是由于時(shí)間緊迫和能力有限,目前 TBSSQL 還是處于 Demo 的階段,離實(shí)現(xiàn)這個(gè)目標(biāo)還有很長(zhǎng)的路要走。
1. Streaming 數(shù)據(jù)源在對(duì)接 Streaming 數(shù)據(jù)源這塊,目前 TBSSQL 有兩個(gè)問(wèn)題。首先,TBSSQL 默認(rèn)輸入數(shù)據(jù)是按照窗口時(shí)間戳嚴(yán)格有序的。這一點(diǎn)在生產(chǎn)環(huán)境中并不一定成立(比如因?yàn)榫W(wǎng)絡(luò)原因,某一段數(shù)據(jù)出現(xiàn)了亂序)。為此,我們需要引入類似 Google MillWheel 系統(tǒng)中 Low Watermark 的機(jī)制來(lái)保證數(shù)據(jù)的有序性。其次,為了保證有序,目前 StreamReader 只能單線程運(yùn)行。在實(shí)際生產(chǎn)環(huán)境當(dāng)中,這里很可能因?yàn)閿?shù)據(jù)消費(fèi)速度趕不上上游數(shù)據(jù)生產(chǎn)速度,導(dǎo)致上游數(shù)據(jù)源的堆積,這又會(huì)反過(guò)來(lái)導(dǎo)致產(chǎn)生計(jì)算結(jié)果的時(shí)間和數(shù)據(jù)生產(chǎn)時(shí)間之間的延遲越來(lái)越大。為了解決這個(gè)問(wèn)題,我們需要將 StreamReader 并行化,而這又要求基于時(shí)間窗口的計(jì)算算子能夠?qū)Χ嗦窋?shù)據(jù)進(jìn)行歸并排序。另外,目前采用 TiDB Global Variable 來(lái)模擬 Streaming 的位置信息,其實(shí)更好地方案是設(shè)計(jì)用一個(gè) TiDB Table 來(lái)記錄每個(gè)不同 StreamReader 讀取到的數(shù)據(jù)位置,這種做法更標(biāo)準(zhǔn)。
2. Planner在 Planner 這塊,從前面的方案介紹可以看出,Streaming 的流式特性和時(shí)序特性決定了 Streaming SQL 的優(yōu)化方式和一般 SQL 有所不同。目前 TBSSQL 的實(shí)現(xiàn)方式是在現(xiàn)有 Planner 的執(zhí)行路徑上加上一系列針對(duì) Streaming SQL 的特殊分支。這種做法很不優(yōu)雅,既難以理解,也難以擴(kuò)展。目前,TiDB 正在基于 Cascade 重構(gòu) Planner 架構(gòu),我們希望今后 Streaming SQL 的相關(guān)優(yōu)化也基于新的 Planner 框架來(lái)完成。
3. 時(shí)間窗口目前,TBSSQL 只實(shí)現(xiàn)了最簡(jiǎn)單的固定窗口。在固定窗口上,Aggregation、Sort 等算子很大程度能復(fù)用現(xiàn)有邏輯。但是在滑動(dòng)窗口上,Aggregation、Sort 的計(jì)算方式和在 Batch Table 上的計(jì)算方式會(huì)完全不一樣。今后,我們希望 TBSSQL 能夠支持完善對(duì)各種時(shí)間窗口類型的支持。
4. 多 Streaming 處理目前 TBSSQL 只能處理單路 Streaming 輸入,比如單個(gè) Streaming 的聚合,排序,以及單個(gè)Streaming 和多個(gè) Table 之間的 Join。多個(gè) Streaming 之間的 Join 因?yàn)樯婕岸鄠€(gè) Streaming 窗口的對(duì)齊,目前 TBSSQL 暫不支持,所以 TBSSQL 目前并不是一個(gè)完整的 Streaming SQL 引擎。我們計(jì)劃今后對(duì)這一塊加以完善。
TBSSQL 是一個(gè)復(fù)雜的工程,要實(shí)現(xiàn) Batch/Streaming 的融合,除了以上提到這四點(diǎn),TBSSQL 還有很有很多工作要做,這里就不一一詳述了。或許,下次 Hackathon 可以再繼續(xù)搞一把 TBSSQL 2.0 玩玩:) 有點(diǎn)遺憾的是作為選手出場(chǎng),沒(méi)有和所有優(yōu)秀的參賽的小伙伴們暢談交流,希望有機(jī)會(huì)可以補(bǔ)上。屬于大家的青春不散場(chǎng),TiDB Hackathon 2019,不見(jiàn)不散~~
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/17869.html
摘要:在上,我司聯(lián)合創(chuàng)始人崔秋帶大家一起回顧了年社區(qū)成長(zhǎng)足跡,在社區(qū)榮譽(yù)時(shí)刻環(huán)節(jié),我們?yōu)樾聲x授予了證書,并為年度最佳貢獻(xiàn)個(gè)人團(tuán)隊(duì)頒發(fā)了榮譽(yù)獎(jiǎng)杯。同時(shí),我們也為新晉授予了證書,并為年最佳社區(qū)貢獻(xiàn)個(gè)人最佳社區(qū)貢獻(xiàn)團(tuán)隊(duì)頒發(fā)了榮譽(yù)獎(jiǎng)杯。 2018 年 TiDB 產(chǎn)品變得更加成熟和穩(wěn)定,同時(shí) TiDB 社區(qū)力量也在發(fā)展壯大。在 TiDB DevCon 2019 上,我司聯(lián)合創(chuàng)始人崔秋帶大家一起回顧了 ...
摘要:我們非常希望本屆誕生的優(yōu)秀項(xiàng)目能夠在社區(qū)中延續(xù)下去,感興趣的小伙伴們可以加入進(jìn)來(lái)哦本文作者是來(lái)自團(tuán)隊(duì)的楊文同學(xué),他們的項(xiàng)目天真貝葉斯學(xué)習(xí)機(jī)在本屆中獲得了三等獎(jiǎng)最佳創(chuàng)意獎(jiǎng)。比賽前一日從廣州南站出發(fā),次日抵達(dá)北京西站。 Ti Hack 系列 TiDB Hackathon 2018 共評(píng)選出六組優(yōu)秀項(xiàng)目,本系列文章將由這六組項(xiàng)目的成員主筆,分享他們的參賽經(jīng)驗(yàn)和成果。我們非常希望本屆 Hack...
閱讀 2267·2021-11-24 11:15
閱讀 3125·2021-11-24 10:46
閱讀 1427·2021-11-24 09:39
閱讀 3950·2021-08-18 10:21
閱讀 1501·2019-08-30 15:53
閱讀 1420·2019-08-30 11:19
閱讀 3354·2019-08-29 18:42
閱讀 2359·2019-08-29 16:58