百万TPS高吞吐、秒级低延迟,阿里搜索离线平台如何实现?
簡(jiǎn)介: 作者 | 鴻歷 作者簡(jiǎn)介:王偉駿,花名鴻歷,阿里巴巴搜索推薦事業(yè)部高級(jí)開(kāi)發(fā)工程師。2016年碩士畢業(yè)于南京郵電大學(xué)。Apache Hadoop && Flink && Eagle Contributor。目前負(fù)責(zé)阿里巴巴搜索離線平臺(tái)Runtime層相關(guān)工作。 另外,陳華曦(昆侖)給了本文很多建議,文中部分圖由李國(guó)鼎(石及)貢獻(xiàn)。
阿里主搜(淘寶天貓搜索)是搜索離線平臺(tái)非常重要的一個(gè)業(yè)務(wù),具有數(shù)據(jù)量大、一對(duì)多的表很多、源表的總數(shù)多和熱點(diǎn)數(shù)據(jù)等特性。對(duì)于將主搜這種邏輯復(fù)雜的大數(shù)據(jù)量應(yīng)用遷移到搜索離線平臺(tái)總是不缺少性能的挑戰(zhàn),搜索離線平臺(tái)經(jīng)過(guò)哪些優(yōu)化最終實(shí)現(xiàn)全量高吞吐、增量低延遲的呢?
前言
在阿里搜索工程體系中我們把搜索引擎、在線算分等ms級(jí)響應(yīng)用戶(hù)請(qǐng)求的服務(wù)稱(chēng)之為“在線”服務(wù);與之相對(duì)應(yīng)的,將各種來(lái)源數(shù)據(jù)轉(zhuǎn)換處理后送入搜索引擎等“在線”服務(wù)的系統(tǒng)統(tǒng)稱(chēng)為“離線”系統(tǒng)。搜索離線平臺(tái)作為搜索引擎的數(shù)據(jù)提供方,是集團(tuán)各業(yè)務(wù)接入搜索的必經(jīng)之路,也是整個(gè)搜索鏈路上極為重要的一環(huán),離線產(chǎn)出數(shù)據(jù)的質(zhì)量和速度直接影響到下游業(yè)務(wù)的用戶(hù)體驗(yàn)。
搜索離線平臺(tái)經(jīng)過(guò)多年沉淀,不僅承載了集團(tuán)內(nèi)大量搜索業(yè)務(wù),在云上也有不少?gòu)椡饪蛻?hù),隨著平臺(tái)功能的豐富,Blink(阿里內(nèi)部版本的Flink) 版本的領(lǐng)先。我們?cè)?019年年初開(kāi)始計(jì)劃把主搜(淘寶天貓搜索)遷移到搜索離線平臺(tái)上。
主搜在遷移搜索離線平臺(tái)之前的架構(gòu)具有架構(gòu)老化、Blink版本低、運(yùn)維困難、計(jì)算框架不統(tǒng)一等不少缺點(diǎn),隨著老主搜人員流失以及運(yùn)維難度與日俱增,重構(gòu)工作早已迫上眉睫。
對(duì)于將主搜這種邏輯復(fù)雜的X億數(shù)據(jù)量級(jí)應(yīng)用遷移到搜索離線平臺(tái)總是不缺少性能的挑戰(zhàn),業(yè)務(wù)特點(diǎn)與性能要求決定了主搜上平臺(tái)的過(guò)程中每一步都會(huì)很艱辛。為了讓性能達(dá)到要求,我們幾乎對(duì)每個(gè)Blink Job都進(jìn)行了單獨(dú)調(diào)優(yōu),最初的理想與最后的結(jié)局都是美好的,但過(guò)程卻是極其曲折的,本文將主要介紹主搜在遷移搜索離線平臺(tái)過(guò)程中在性能調(diào)優(yōu)方面具體做了哪些嘗試。
主搜遷移搜索離線平臺(tái)的完成對(duì)于平臺(tái)來(lái)說(shuō)有里程碑式的意義,代表搜索離線平臺(tái)有能力承接超大型業(yè)務(wù)。
搜索離線平臺(tái)基本概念
搜索離線平臺(tái)處理一次主搜全增量主要由同步層和數(shù)據(jù)處理層組成,它們又分別包括全量和增量流程。為了讀者更好理解下文,先簡(jiǎn)單介紹幾個(gè)關(guān)于搜索離線平臺(tái)的基本概念。
集團(tuán)內(nèi)支撐業(yè)務(wù)
目前搜索離線平臺(tái)在集團(tuán)內(nèi)支持了包括主搜,AE在內(nèi)的幾百個(gè)業(yè)務(wù)。其中數(shù)據(jù)量最大的為淘寶天貓?jiān)u價(jià)業(yè)務(wù),數(shù)據(jù)量達(dá)到了X百億條,每條數(shù)據(jù)近上X個(gè)字段。
場(chǎng)景
處理用戶(hù)的數(shù)據(jù)源(mysql或odps)表,將數(shù)據(jù)經(jīng)過(guò)一系列的離線處理流程,最終導(dǎo)入到Ha3在線搜索引擎或ES中。
平臺(tái)相關(guān)技術(shù)棧
如下圖,搜索離線平臺(tái)目前數(shù)據(jù)存儲(chǔ)基于HDFS/盤(pán)古,資源調(diào)度依賴(lài)于YARN或Hippo,計(jì)算框架統(tǒng)一用Flink/Blink執(zhí)行。
全量
全量是指將搜索業(yè)務(wù)數(shù)據(jù)全部重新處理生成,并傳送給在線引擎,一般是每天一次。
這么做有兩個(gè)原因:有業(yè)務(wù)數(shù)據(jù)是Daily更新;引擎需要全量數(shù)據(jù)來(lái)高效的進(jìn)行索引整理和預(yù)處理,提高在線服務(wù)效率。全量主要分為同步層與數(shù)據(jù)處理層。
增量
增量是指將上游數(shù)據(jù)源實(shí)時(shí)發(fā)生的數(shù)據(jù)變化更新到在線引擎中。
這也就意味著在我們的場(chǎng)景中對(duì)于增量數(shù)據(jù)不需要保證Exactly Once語(yǔ)義,只需要保證At Least Once語(yǔ)義。基于該背景,我們才能用全鏈路異步化的思維來(lái)解一對(duì)多問(wèn)題(下文會(huì)詳細(xì)講解)。
與全量一樣,增量也分為同步層與數(shù)據(jù)處理層。
一對(duì)多
在搜索這個(gè)領(lǐng)域某些業(yè)務(wù)數(shù)據(jù)需要用一對(duì)多的形式來(lái)描述,比如商品寶貝和SKU的關(guān)系即是個(gè)典型的一對(duì)多數(shù)據(jù)的例子。在搜索離線基于Hologres(阿里巴巴自研分布式數(shù)據(jù)庫(kù))存儲(chǔ)的架構(gòu)中,一對(duì)多的數(shù)據(jù)存儲(chǔ)在單獨(dú)的一張雙pk的HoloTable中,第一、二主鍵分別的寶貝ID與SKU_ID。
有了上面這些概念之后,在后續(xù)的段落中我們會(huì)看到搜索離線平臺(tái)針對(duì)主搜各Blink Job的性能調(diào)優(yōu),先簡(jiǎn)要概括下主搜業(yè)務(wù)特點(diǎn)與性能要求。
**數(shù)據(jù)存儲(chǔ)方式
**
搜索離線平臺(tái)以前用HBase做鏡像表時(shí),是用一張多列族大寬表來(lái)存儲(chǔ)業(yè)務(wù)單維度所有數(shù)據(jù)。經(jīng)過(guò)詳細(xì)調(diào)研之后,我們決定用Hologres替換HBase,所以需要對(duì)存儲(chǔ)架構(gòu)做全面的重構(gòu)。用多表來(lái)模擬HBase中的多列族,單HoloTable中包括很多業(yè)務(wù)數(shù)據(jù)源表的數(shù)據(jù)。重構(gòu)后的數(shù)據(jù)存儲(chǔ)方式大致如下:
同步層
所謂同步層,一般是將上游數(shù)據(jù)源的數(shù)據(jù)同步到鏡像表,供數(shù)據(jù)處理層高效處理。由于業(yè)務(wù)方單維度的數(shù)據(jù)有很多Mysql表或odps表組成,少則X張,多則像主搜這樣X(jué)張。所以將同緯度數(shù)據(jù)聚合到一張Holo表中時(shí),如果多張表兩兩join的話會(huì)產(chǎn)生大量shuffle,所以我們采取異步upsert方式,不同數(shù)據(jù)源表的數(shù)據(jù)寫(xiě)Holo表中不同的列來(lái)解決海量數(shù)據(jù)導(dǎo)入問(wèn)題。
數(shù)據(jù)處理層
所謂數(shù)據(jù)處理層,是指將同步層得到的各鏡像表(HBase/Holo)的數(shù)據(jù)進(jìn)行計(jì)算,一般包括多表Join、UDTF等,以方便搜索業(yè)務(wù)的開(kāi)發(fā)和接入。
主搜業(yè)務(wù)特點(diǎn)與性能要求
下面首先介紹下主搜業(yè)務(wù)特點(diǎn)與性能要求,再詳細(xì)介紹我們進(jìn)行了怎樣的調(diào)優(yōu)才達(dá)到了性能的要求。
主搜業(yè)務(wù)特點(diǎn)
★ 數(shù)據(jù)量大
主搜有X億(有效的X億)個(gè)商品,也就是主維度有X億條數(shù)據(jù),相比于平臺(tái)其他業(yè)務(wù)(除淘寶評(píng)價(jià)業(yè)務(wù))多出X個(gè)數(shù)量級(jí)。這么多數(shù)據(jù)我們能否在X個(gè)多小時(shí)完成全量?如何實(shí)現(xiàn)高吞吐?挑戰(zhàn)非常大。
★ 一對(duì)多的表很多
主搜業(yè)務(wù)有很多一對(duì)多的表需要Join,例如一個(gè)商品對(duì)應(yīng)多個(gè)SKU,部分商品對(duì)應(yīng)了接近X個(gè)SKU信息。這些信息如何能夠高性能的轉(zhuǎn)換為商品維度,并與商品信息關(guān)聯(lián)?
★ 源表的總數(shù)多
主搜有X多張表(包括一對(duì)多的表),平臺(tái)其他業(yè)務(wù)的源表個(gè)數(shù)一般都在個(gè)位數(shù)。源表數(shù)量多會(huì)導(dǎo)致一系列的問(wèn)題,比如讀取ODPS數(shù)據(jù)時(shí)如何避免觸發(fā)ODPS的限制?拉取大表數(shù)據(jù)時(shí)如何做到高吞吐?這些問(wèn)題都需要我們一一解決。
★ 熱點(diǎn)數(shù)據(jù)
主搜有一些大賣(mài)家(餓了么,盒馬等)對(duì)應(yīng)了很多商品,導(dǎo)致在數(shù)據(jù)處理層出現(xiàn)非常嚴(yán)重的數(shù)據(jù)傾斜等問(wèn)題。如何解決大數(shù)據(jù)處理方向經(jīng)常出現(xiàn)的SKEW?
主搜性能要求
★ 全量(同步層 + 數(shù)據(jù)處理層)高吞吐!
全量要求每天一次,在有限的資源情況下每次處理X億的商品,這么大的數(shù)據(jù)量,如何實(shí)現(xiàn)高吞吐,挑戰(zhàn)非常大!
★ 增量(同步層 + 數(shù)據(jù)處理層)低延遲!
增量要在Tps為X W的情況下達(dá)到秒級(jí)低延遲,并且雙11期間有部分表(例如XX表)的Tps能達(dá)到X W,增量如何保證穩(wěn)定的低延遲?值得思考!
下面一一描述我們是如何解決這些問(wèn)題來(lái)達(dá)到性能要求的。
Blink Job性能調(diào)優(yōu)詳解
根據(jù)上述主搜業(yè)務(wù)特點(diǎn)與性能要求羅列出下圖,左邊與中間兩列表示主搜哪些特點(diǎn)導(dǎo)致某階段任務(wù)性能差。所以我們要對(duì)相應(yīng)階段Blink Job進(jìn)行調(diào)優(yōu),調(diào)優(yōu)完成也就代表著平臺(tái)能滿足圖中最右邊一列主搜所需要的全量高吞吐與增量低延遲的性能要求。
下面按照全量,增量,解一對(duì)多問(wèn)題的脈絡(luò)來(lái)給大家介紹我們是如何解決上述五個(gè)問(wèn)題之后達(dá)到全量高吞吐以及增量低延遲的性能要求的。
全量高吞吐性能調(diào)優(yōu)
全量主要包括同步層與數(shù)據(jù)處理層,必須實(shí)現(xiàn)高吞吐才能讓全量在X個(gè)多小時(shí)之內(nèi)完成。同步層在短時(shí)間內(nèi)要同步約X張表中的上X億全量數(shù)據(jù),且不影響同時(shí)在運(yùn)行的增量時(shí)效性是一個(gè)巨大的挑戰(zhàn)。數(shù)據(jù)處理層要在短時(shí)間內(nèi)處理X多億條數(shù)據(jù),Join很多張鏡像表,以及UDTF處理,MultiGet等,最后產(chǎn)生全量HDFS文件,優(yōu)化過(guò)程一度讓人頻臨放棄。這里重點(diǎn)介紹數(shù)據(jù)處理層的性能調(diào)優(yōu)歷程。
該Job的調(diào)優(yōu)歷時(shí)較長(zhǎng),嘗試方案較多,下面按照時(shí)間順序講解。
★ 初始形態(tài)
首先提一下IC維度為商品維度,UIC維度為賣(mài)家維度,并且最開(kāi)始我們的方案是沒(méi)有FullDynamicNestedAggregation和IncDynamicNestedAggregation的(后文會(huì)詳細(xì)提到這兩個(gè)Job)。Scan IC維度單Pk表之后做一系列的DImJoin、UDTF、MultiJoin。在測(cè)試過(guò)程中發(fā)現(xiàn)DimJoin多pk表(一對(duì)多表)的數(shù)據(jù)時(shí),性能非常低下,全鏈路Async的流程退化成了Sync,原因是我們一對(duì)多的數(shù)據(jù)存在單獨(dú)的一個(gè)SaroTable(對(duì)多個(gè)HoloTable的邏輯抽象)中,對(duì)指定第一pk來(lái)取對(duì)應(yīng)所有數(shù)據(jù)用的是Partial Scan,這是完全Sync的,每Get一次都要?jiǎng)?chuàng)建一個(gè)Scanner,雖然我們不但對(duì)于DimJoin加了Cache,并且對(duì)于主搜特有的MultiGet也加了對(duì)于SubKey的精準(zhǔn)Cache。但是測(cè)試下來(lái)發(fā)現(xiàn),性能還是完全得不到滿足,所以嘗試?yán)^續(xù)優(yōu)化。
★ 引入LocalJoin與SortMergeJoin
由于性能瓶頸是在DimJoin多pk的SaroTable這里,所以我們想辦法把這部分去掉。由于一對(duì)多的SaroTable只有兩個(gè)維度具有,所以我們嘗試先分別將IC維度與UIC維度的所有表(包括單pk與多pk)進(jìn)行LocalJoin,結(jié)果再進(jìn)行SortMergeJoin,然后繼續(xù)別的流程。
首先介紹下Local Join。由于HoloStore保證相同DB中所有表都是按照相同的Partition策略,并且都是按照主鍵字典序排好序的,所以我們可以將同緯度同Partition的數(shù)據(jù)拉取到一個(gè)進(jìn)程中進(jìn)行Join,避免了Shuffle,如下圖所示。
所以拓?fù)浯蟾抛優(yōu)?#xff1a;
經(jīng)過(guò)測(cè)試,由于業(yè)務(wù)上面存在大賣(mài)家(一個(gè)賣(mài)家有很多商品),導(dǎo)致SortMergeJoin之后會(huì)有很?chē)?yán)重的長(zhǎng)尾,如下圖所示,Uid為101與103的數(shù)據(jù)都是落到同一個(gè)并發(fā)中,我曾經(jīng)嘗試再這個(gè)基礎(chǔ)之上再加一層PartitionBy nid打散,發(fā)現(xiàn)無(wú)濟(jì)于事,因?yàn)镾ortMergeJoin的Sort階段以及External Shuflle對(duì)于大數(shù)據(jù)量的Task需要多次進(jìn)行Disk File Merge,所以該長(zhǎng)尾Task還是需要很長(zhǎng)時(shí)間才能Finish。
★ 加鹽打散大賣(mài)家
所以我們需要繼續(xù)調(diào)優(yōu)。經(jīng)過(guò)組內(nèi)討論我們決定對(duì)大賣(mài)家進(jìn)行加鹽打散,從ODPS源表中找出Top X的大賣(mài)家ID,然后分別在主輔維度Scan + Local Join之后分別加上UDF與UDTF,具體流程圖與原理示例見(jiàn)下面兩幅圖:
如上圖所示,Uid為101與103的數(shù)據(jù)被打散到多個(gè)并發(fā)中了,并且因?yàn)槲覀冊(cè)赟ortMergeJoin之后加了UDTF把加的Salt去掉,所以最終數(shù)據(jù)不會(huì)有任何影響。
★ 最終形態(tài)
這樣全量FullJoin總算完成了,并且性能也勉強(qiáng)達(dá)標(biāo),所以我們開(kāi)始調(diào)整增量流程(IncJoin),這時(shí)發(fā)現(xiàn)IncJoin跟FullJoin的初始形態(tài)存在一樣的問(wèn)題,追增量非常慢,永遠(yuǎn)追不上,所以組內(nèi)討論之后決定在同步層針對(duì)全量新增一個(gè)FullDynamicNestedAggregation Job(下文會(huì)詳細(xì)提到),這是一個(gè)Blink Batch Job它將各維度一對(duì)多的SaroTable數(shù)據(jù)寫(xiě)到對(duì)應(yīng)維度的主表中,然后在FullJoin最開(kāi)始Scan時(shí)一起Scan出來(lái),這樣就避免了DimJoin多pk的SaroTable。最終達(dá)到了全量高吞吐的要求,全量FullJoin最終形態(tài)如下:
增量低延遲性能調(diào)優(yōu)
增量性能主要受困于數(shù)據(jù)處理層IncJoin,該Job最開(kāi)始是一個(gè)Blink Stream Job,主要是從SwiftQueue中讀出增量消息再關(guān)聯(lián)各個(gè)鏡像表中的數(shù)據(jù)來(lái)補(bǔ)全字段,以及對(duì)數(shù)據(jù)進(jìn)行UDTF處理等,最后將增量消息發(fā)往在線引擎SwiftQueue中。
基于“流批一體”的思想,經(jīng)過(guò)一系列嘗試,我們?cè)隽繑?shù)據(jù)處理層Job的最終形態(tài)如下。
與全量不同的是由于增量是實(shí)時(shí)更新的,所以更新記錄不僅要寫(xiě)到Swift Queue中,還要寫(xiě)入SaroTable中。另外,我們根據(jù)業(yè)務(wù)特點(diǎn)給各個(gè)Job分別加了按pk對(duì)記錄去重的window。
解一對(duì)多問(wèn)題
主搜有很多一對(duì)多的表,在數(shù)據(jù)處理層如何高效的將數(shù)據(jù)Get出來(lái)轉(zhuǎn)換為主維度之后進(jìn)行字段補(bǔ)全,困擾我們很久。
為了提升效率我們必須想辦法提升Cpu利用率。所以Get記錄改為全鏈路異步來(lái)實(shí)現(xiàn),由于我們一對(duì)多數(shù)據(jù)存在多pk的HoloTable中,指定第一pk去獲取相關(guān)數(shù)據(jù)在Holo服務(wù)端是以Scan來(lái)實(shí)現(xiàn)的。這樣由于異步編程的傳染性,全鏈路異步會(huì)退化為同步,性能完全不達(dá)標(biāo)。
★ 解決方法
為了將“偽異步”變成真正的全鏈路異步,經(jīng)過(guò)多次討論與實(shí)踐之后,我們決定將一對(duì)多表中相同第一pk的多條數(shù)據(jù)Scan出來(lái)GroupBy為一條數(shù)據(jù),將每個(gè)字段轉(zhuǎn)化為Json之后再Put進(jìn)主表中,主要步驟如下圖所示。
我們針對(duì)全量與增量在同步層加Job來(lái)解決,分別為FullDynamicNestedAggregation(Blink Batch Job)與IncDynamicNestedAggregation(Blink Stream Job),這兩個(gè)Job大致流程為如下圖所示。
值得一提的是,正如前文介紹增量時(shí)提到的背景,我們的場(chǎng)景中對(duì)于增量數(shù)據(jù)不需要保證Exactly Once語(yǔ)義,只需要保證At Least Once語(yǔ)義。所以基于該背景,我們能夠?qū)?shù)據(jù)處理層增量Job拆分為兩個(gè)Job執(zhí)行,一對(duì)多的問(wèn)題得以解決。
這樣我們?cè)跀?shù)據(jù)處理層就不需要去Scan HoloTable了,從而可以用全鏈路異步化的方式來(lái)提升增量整體性能。
★ 截?cái)鄡?yōu)化
為了避免將多條數(shù)據(jù)轉(zhuǎn)為一條數(shù)據(jù)之后由于數(shù)據(jù)量過(guò)大導(dǎo)致FullGC的“大行”問(wèn)題。基于業(yè)務(wù)的特性,我們對(duì)于每個(gè)一對(duì)多表在Scan時(shí)支持截?cái)喙δ?#xff0c;對(duì)于相同的第一pk記錄,只Scan一定條數(shù)的記錄出來(lái)組裝為Json,并且可以針對(duì)不同的表實(shí)現(xiàn)白名單配置。
★ 加過(guò)濾Window優(yōu)化
針對(duì)業(yè)務(wù)的特點(diǎn),一對(duì)多的很多表雖然可以接受一定時(shí)間的延遲,但是為了避免對(duì)離線系統(tǒng)以及在線BuildService造成太大的沖擊,所以更新不能太多,所以我們加了30min的去重窗口,這個(gè)窗口作用非常大,平均去重率高達(dá)X%以上。
結(jié)語(yǔ)
經(jīng)過(guò)一系列優(yōu)化,主搜不僅在資源上相對(duì)于老架構(gòu)有不少的節(jié)省,而且同時(shí)實(shí)現(xiàn)全量高吞吐與增量低延遲,并且在2019年度雙11 0點(diǎn)應(yīng)對(duì)突增流量時(shí)表現(xiàn)的游刃有余。
對(duì)系統(tǒng)進(jìn)行性能調(diào)優(yōu)是極其復(fù)雜且較精細(xì)的工作,非常具有技術(shù)挑戰(zhàn)性。不僅需要對(duì)所選用技術(shù)工具(Flink/Blink)熟悉,而且對(duì)于業(yè)務(wù)也必須了解。加window,截?cái)鄡?yōu)化,加鹽打散大賣(mài)家等正是因?yàn)闃I(yè)務(wù)場(chǎng)景能容忍這些方法所帶來(lái)的相應(yīng)缺點(diǎn)才能做的。
除了本文提到的調(diào)優(yōu)經(jīng)驗(yàn),我們對(duì)同步層全增量Job與MultiGet也進(jìn)行了不少調(diào)優(yōu),篇幅原因與二八原則這里就不詳細(xì)介紹了。
主搜成功遷移也使得搜索離線平臺(tái)完成了最后一塊拼圖,成為阿里巴巴集團(tuán)搜索中臺(tái)以及核心鏈路的基礎(chǔ)模塊。
總結(jié)
以上是生活随笔為你收集整理的百万TPS高吞吐、秒级低延迟,阿里搜索离线平台如何实现?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 阿里云RDS vs 自建MySQL,这篇
- 下一篇: 基于 Knative 打造生产级 Ser