天天看點

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

導讀:首先跟大家解釋一下,大家在看到海報的時候,上面寫的是HermesDB,為了規避一下法律風險,現在我們正式把他改名成了MercsDB,它們是同一個引擎的不同的說法。

全文按照以下五個方面來展開:

  • 背景
  • 存儲
  • 計算
  • 測試及應用
  • 未來規劃

01

MercsDB背景介紹

騰訊在過去十幾年的發展中,對資料倉庫有着非常大的需求,他們的要求可以說是非常複雜。

首先是資料的需求:

  • 單表可以存在上千列,百億行,非常的大。
  • 需要對任意列中的任意行進行索引,而且可能沒有主鍵
  • 實時更新,實時寫入
  • 根據不同場景,不同需求,進行列存或行存
  • 索引需求旺盛,需要既适合點查,有适用範圍查詢,還會有空間中的位址查詢

除了資料的複雜性和變化之外,還對性能有很高的的需求:

  • 百億行的查詢秒級響應
  • 既要支援互動式查詢,也支援報表分析
  • 寫入要求很高,每天要寫入千億行

是以MercsDB業務場景非常複雜,基本涵蓋我們業界可以說是主流的查詢場景,和我們現在主流引擎,比如presto、impala、es或MercsDB既有相似之處,又有不同之處:

  • 相似之處:MercsDB本身也是一個MPP架構的引擎,而且他們覆寫的這些場景,presto、es、ck等引擎都會覆寫到。
  • 不同之處:MercsDB總體來講是一個非常綜合的引擎,它要求非常的全面,不是對使用者的某一個方面進行優化。
騰訊新一代多元分析引擎MercsDB在微信支付上的應用

1. 發展曆史

那清楚了這些需求,我們來看下MercsDB的曆史,2013年就開始在騰訊内部進行孵化,迄今為止已經9年多接近10年了,那他先後經曆了4個版本,在之前我們都稱之為Hermes1.0、2.0、3.0,到我們4.0這個版本改成了MercsDB,那麼為什麼會這麼多版本呢,其實主要是騰訊内部對實時分析有這逐漸提升的需求:

  • 2013.01-2016.12,Hermes1.0:第一個版本主要是使用者畫像這種常見的bi場景,需要做實時的OLAP分析,當時我們主要使用反向索引,支援了一些基礎的OLAP場景。
  • 2016.12-2019.06,Hermes2.0:這一年需要對日志進行分析,日志的特點是資料量非常大,騰訊的業務上還需要實時更新,是以就進一步擴充了能力。
  • 2019.06-2021.12,Hermes3.0:這個時候廣告業務也想用MercsDB,是以進一步引入了Spark支援了一個完整的OLAP能力,這時不隻能完成簡單的聚合和點查功能,還能進行複雜的JOIN、視窗函數的查詢。
  • 20221.12至今,Hermes4.0(MercsDB):這個時候因為像ck這些MPP引擎發展非常迅速,大家對性能的要求就越來越高了,是以在4.0我們立志要把性能優化上去,比如說把Spark替換成Presto,做一些向量化的改造,這就誕生了今天的MercsDB。

可以說MercsDB經過了這10年的研發改造,成為了之前的各個特征的集大成者,性能也達到了一個非常理想的狀态。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

2. 目前使用情況

這個是MercsDB在騰訊内部的使用情況:

  • 整個叢集有5000+節點
  • 每天查詢在千萬級别
  • 存儲總量在百PB左右,每天新增在1PB的資料
  • 高峰期單機查詢每秒處理1億行的寫入

MercsDB目前在騰訊主流APP下基本都有使用,比如微信、qq、騰訊視訊、騰訊遊戲以及廣告财付通等。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

--

02

存儲架構

介紹完背景,我們來介紹一下MercsDB的核心,我們先從存儲開始介紹。

這是一個基礎的架構,上面是計算引擎,下面是存儲,可以注意到MercsDB的存儲以下兩種。

(1)Local:本地磁盤,和jvm放在同一台機器上

①低延遲,适合對于延遲敏感,資料量不是特别大的業務。

②服務有損,如果某台機器挂了,會缺失部分資料。

(2)遠端存儲:hdfs,ceph、Ozone

①資料可靠,有多個副本

②有容錯,還支援異構

選擇方法可以根據具體的業務模式來選擇具體的存儲模式,比如

  • 是否需要HA高可用
  • 是否需要極低的延遲
  • 是否是熱資料,要每天都能查到,還是說冷熱分離的,隻查少量資料,大部分都查不到
騰訊新一代多元分析引擎MercsDB在微信支付上的應用

1. 列存和索引概況

MercsDB還在列存和索引上做了很多優化,主要還是跟業務的需求有關:

  • 查詢非常簡單,但是QPS非常高
  • 查詢資料量非常大,比如百億行的資料,但是需要秒級内響應
  • 資料比較海量,對成本非常敏感,比如說日志服務,需要MercsDB能在成本上進行一些優化

不同的使用者有不同的業務需求,是以對MercsDB的資料和索引就需要進行多方面的優化。

(1)資料存儲

  • 檢索類型的主要支援低延遲查詢
  • 排序類型的主要針對海量資料
  • 壓縮類型的主要是節省成本
  • 嵌套類型的主要支援一些像parquet或類似于parquet這樣的存儲

(2)資料索引

  • 稀疏索引
  • 跳表索引
  • 反向索引
  • 點查索引
  • LBS索引

下面我們先來針對這些索引進行詳細的介紹。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

2. 檢索列

檢索型列查是OLAP場景最常見的列存方式,使用者資料量可能不是特别大,大概就是百G,并且查詢非常簡單,單表聚合或點查,但是要求延遲非常低,這時可以用檢索列存。

這個類型使用了空間換時間的思想,添加了很多索引。比如右邊這個圖,使用者需要根據某個值對原來的資料進行檢索,先擷取到rowId,然後根據rowId擷取offset,再通過offset拿到label(這裡是字典的label,不會直接存儲string類型字段的值),之後他再根據label拿到資料。

這樣在檢索的中間計算過程中,隻要用rowId或label進行一個代表就可以了,不需要用到string字段的元素值,這樣整個計算或整個檢索就會非常快。

按照目前使用情況來看,所有列都進行索引的情況下,索引使用的空間占比大概在40%左右,這裡就是利用對資料進行索引支付一些額外的開銷,進而帶來一些比較不錯的性能提升。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

3. 排序列

在日志分析的場景中,要分析海量的資料,是以資料量會非常的大,但是使用者的查詢模式有點查也有聚合,列也沒有主鍵,資料也沒有特殊的特征,可能任意一列都會被查到,是以就沒有一個特别好的加速方法,這個場景就推薦使用者使用排序列。

好處是會盡量把相似的列放在一起,不管是從檢索角度講,還是從存儲壓縮角度講,性能都比較好。

比如說圖檔右邊這種情況,有一個對元素值的索引ValueIndex,它是一個排序索引,另外對資料的還增加了一個offset的索引OffsetIndex,這樣就相當于有兩個索引,對資料的索引和對資料值位置的索引,這樣在檢索的時候性能非常的高。

在日志服務的場景中,使用排序列存的方式,性能能達到不做排序的10倍。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

4. 壓縮列

這種列适合那種高基數的列,比如說使用者的很多标簽值,或者metric值,這類資料有兩個特點:

  • 沒有固定的特征,也不像性别、地區這種基數比較少的,它可能是成千上萬,甚至上千萬個,基數特别高。
  • 直接壓縮性能很低,這種編碼沒有什麼特征,比如對string進行編碼,編碼的結果可能沒有有序性或聚合性這種特征。

這種的就可以采用壓縮列,這用BitShuffle+lz4這種壓縮,lz4是一個很簡單而且應用範圍很廣的壓縮方法。

BitShuffle是什麼呢,比如我們要存儲4、5、8、9(可能是string,也可能是double),轉換成二進制辨別之後,聯系非常少,仔細觀察可以發現高幾位全是0,如果順序排列存在檔案裡,壓縮性能會非常差,那如果放在一起,根據第一位對齊,可以發現前面大概十幾位都是0,對這些資料進行壓縮,性能就會非常好。

使用BitShuffle+lz4的方式和老版(bitpacking壓縮)相比性能提升非常明顯,至少提高50%,準确地說存儲所需容量降低一倍。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

5. 索引

除了列存之外,MercsDB還有很多索引:

  • 反向索引:主要用來進行點查或檢索。
  • FST索引:主要用于處理字元串,類似Trie樹,Trie樹是處理字元串非常有用的一個工具,FST可以了解為是Trie樹的一個更新版。
  • KDB的索引:主要對空間/location查詢會有很大的性能提升。
騰訊新一代多元分析引擎MercsDB在微信支付上的應用

--

03

計算架構

剛剛介紹了很多存儲方面的東西,接下來介紹一下本次的重頭戲,計算。

這張圖是整個MercsDB的計算流程。

(1)上面是接口層,可以通過jdbc、http、grpc等多種方式接入

(2)中間是路由層:

① SQL route路由層:通過對SQL的簡單的解析和判斷,自動為使用者選擇是使用左邊的還是右邊的引擎。

② 兩套引擎,自研的native引擎和開源的presto引擎。

  • native引擎:原生的查詢引擎,完全自研,沒有借助任何正常的mpp的引擎,适合簡單的query,比如單表的點查和聚合,要求查詢場景比較簡單和固定。
  • presto引擎:為了支援完整的OLAP場景。

(3)最終将計算通過mpp架構下推到下面的worker上

雖然有兩個引擎,但都是用java編寫的,是以代碼都在同一個jvm裡面,可以了解為native和presto都在同一個java程序裡的,接下來我們就詳細介紹一下這兩個引擎。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

1. Native引擎

Native引擎對将列存或者是索引進行計算,先做過濾(圖有些問題),做完過濾之後,通過本地索引進行聚合。

比如計算count,sum,min和max,在Native模式進行聚合時,不論聚合哪一列,都會把最大值,最小值同時傳回回來,如果你隻需要sum,那我們就把sum的值,傳回給你。

總結一下就是,通過索引加上預處理的聚合,最終将這個結果傳回給使用者。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

2. 延遲物化

另外一個是在查詢過程中為了加速性能,做的延遲物化。

比如使用者想以作業系統聚合查詢一個點查,真正查詢時,引擎不會用os列進行查詢,會内部對os列的值做一個标簽,它是一個123這種數值類型的标簽,先對标簽進行聚合,再對标簽進行解碼,最後傳回給使用者。

這樣的好處是在進行聚合的時候,記憶體消耗比直接用string消耗小很多,性能也要高很多。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

3. Presto引擎

另外就是presto引擎,這裡主要是用到了presto的connector,把MercsDB原生的查詢模式接入了presto。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

4. 算子下推

接入肯定也不是簡單的接入,這裡也做了很多優化。

首先是算子下推,把很多SQL的算子,都下推到MercsDB之中。

目前下推了5種算子,filter、agg、limit、order等,如果用傳統的優化模式,需要寫5條規則,比較麻煩。

我們發現在下推時有一個特征,下推的算子一般都是連續的,一般都靠近葉子節點,比如說這一系列算子都可以下推,如果有一個算子,比如說filter下推不了,那上面的算子都不應該下推。

是以在周遊這棵AST的時候,會生成一個Query Generator的棧,發現算子可以下推就把它推到棧中,假如下面的算子依舊可以下推,就繼續推到棧中,一直到Table Scan算子,說明整個Generator棧中算子都能下推到MercsDB中,這時會把棧中的算子轉化成一個MercsDB的計算節點。

還有一種情況,比如下推到filter,發現filter的某一個過濾條件,非常複雜,沒辦法下推,這說明之前的所有算子都不能下推,就會把棧清空。

這樣做的好處是:

  • 規則非常簡單,隻需要寫一條規則。
  • 整個ATS的周遊,隻需要周遊一遍,而傳統方式則需要寫5條規則,總共需要周遊5遍。

這就是算子下推,這是第一點。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

5. 資料轉換

在MercsDB中把資料讀取出來計算好之後,要傳回給presto,presto原生支援把外來的資料轉換為presto block,用的是block builder,這個是一個接口,有很多方法,比如說writeInt、writeBytes等等。正常類型都是通過接口的抽象方法,寫到各種presto底層庫之中的。

這樣就會有兩個問題:

(1)這是個虛函數,虛函數的調用性能非常差

(2)不能很好的利用原來資料的存儲格式

這裡如果深入的解構一下presto和MercsDB的資料,就會發現presto的block隻存了兩類資料:

(1)第一個是value數組

很多人可能會問,為啥是long數組,不是還有很多double、float等等的資料類型,這個是java有讀裸二進制資料的能力,64位以内的類型比如說double、float、int等都可以用long或int數組來存,而MercsDB的api傳回給presto或其他引擎的資料也是這個類型,是以這裡直接使用了MercsDB的資料。

(2)第二個是isNulls數組

它對應着位是不是為空,這個在MercsDB中是沒有的,是以我們通過向量化的方法,寫了一個向量化的isNulls的api,也就是把values這個數組作為一個整體傳給MercsDB,MercsDB通過向量化計算,直接計算出某一位是否為空,具體怎麼計算呢,其實就是把列存或索引的方法用向量化的方法,翻譯了過來,這樣在資料加載上,性能也比較好。

可以在直接建構和builder的對比中看到,優化後的性能也有一定提升,可以說做了這兩步的優化後,presto和MercsDB的融合就做的非常好了。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

6. Java Vector API

除此之外,還做了一些顯示向量化的優化,這裡介紹下MercsDB使用的Java Vector API。

presto也好,MercsDB也好,都是java寫的,是以在向量化方法上是沒有像ck這種引擎可以使用C++做向量化優化,MercsDB仍然采用的是java生态,這主要是引擎曆史和騰訊這邊的開發要求來選擇的。

那Java Vector API實際上是Java在孵化的一個feature,是從jdk16開始的,jvm通過一些api能夠向量化的調用一些底層CPU的AVX等等的這些SIMD指令集,這樣可以批量的進行一些計算。

比如這裡有很多Vector,可能是512位的,也可能是256的。由于float是32位的,512位意味着可以存16個float類型的資料,這說明在一個CPU指令中,可以對16個float資料進行計算,這樣就達到了提升接近16倍效率的效果,當然實際上達不到16倍性能那麼好,但是依然有成倍的性能的提升,這個就是Java Vector API。

我可以舉個例子,右邊上方這個就是标量版本的一個寫法,一個很簡單的寫法,右邊下方就是使用Vector API方式的寫法,使用fromArray進行加載,然後計算,最後還原到數組中。

具體的Vector API大家可以參考Java官網,這裡需要注意下MercsDB使用的是騰訊自研的KONA JDK,這個是開源的,因為KONA JDK的Vector API在騰訊的其他業務線上已經有了比較長時間的積累了,是以相對而言我們還是相對更加信任一下我們内部的這個版本。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

7. 向量化優化

在MercsDB内部的很多代碼進行的Vector API的改造,比如說這個decode函數。不過這裡就不帶着大家讀代碼了,主要來總結一下過去幾個月,用Vector API對Java原生API的改造,遇到的一些經驗教訓。

第一個是,在做Vector API時,盡量用循環展開的技術,比如這裡可能隻有一個資料,但是我們希望多提取幾個資料,這樣盡量利用JVM或JIT對Java Code的優化,這就可以通過循環展開,對這段代碼進行預熱,進而使用JIT對這段代碼進行性能優化。

第二個是,在整個計算中,我們使用到的Java Vector的類型是一緻的,比如說計算最開始傳進來的是一個long類型的數組,但計算到一半的時候要把類型轉換成int,傳回的時候又要轉成long類型,如果按照原生的Java 代碼進行改寫,意味着既要用到long Vector又要用到int Vector,而且還涉及到這兩者的轉換。

這沒有必要且性能還差,是以建議在代碼裡盡量保持一緻,不要做Vector的轉換,那可能很多同學會問,那這塊代碼怎麼辦呢,畢竟有一些轉換,我們經過研究之後發現都是正數或者是無符号的,是以可以直接忽略long類型的前面的32位,直接對後面32位進行操作,是以我們通過源碼的方式或者通過位運算的方式,直接把整個計算轉換成long類型,遇到int類型的計算時,把前面的高位進行忽略。

第三點值得注意的就是計算本身,計算中不要做一些額外操作,比如裝箱開箱;還有對象建立,包括long Vector數組的建立,盡量都不要有,都放在代碼的外部,進行複用;還有函數調用,盡量把邏輯都放在一起。

這些是在Vector改造時的一些經驗,做完這些改造之後,可以從benchmark中看到,改造前後性能提升還是比較明顯的。

這隻是向量化改造中的一點,實際上改造的非常多,隻在這舉了一個例子。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

8. 其他優化

除此之外,還有一些其他的優化。

第一個是把原來MercsDB中很多單次調用的函數,都改成批處理的,減少虛函數調用。

第二個是把string還原給presto,因為presto最後傳回給使用者的是一個真正的string,但在中間計算、傳輸的過程中其實傳回的是一個index,此時就需要把index轉為string,但這個操作很耗時,原因是使用者在存string的時候,順序是随機的,字典在記憶體中進行查詢轉換的時候,也是一個随機記憶體通路,是以這裡把index做了一個排序,不是原始的資料排序,是在計算的時候,在最後轉換成string之前,我們對index做了一個排序,這樣對字典的通路就是一個順序的通路了,通路完之後,再還原成原來的順序。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

9. 優化前後性能對比

計算方面的優化就講到這,我們做了這麼多優化,包括把Presto引入進來和把Vector引入進來,接下來看下測試的benchmark。

這裡是用的SSB進行的測試,這個SSB是一個星型benchmark,應該是一個比較知名的,表用的是flat_lineorder的表,使用了6億和60億的資料,大概是200G和2T。

第一個比較的是優化前後的MercsDB對比,藍色的是優化前的,橙色的是優化前的,可以看到不管是6億還是60億資料,都是非常的明顯的,而且是整體都有一個不錯的性能提升。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

10. 單機查詢和CK性能對比

第二個是和目前主流的引擎ck做的一個對比,可以看到MercsDB在很多産品下也是要比ck快的,這裡要提一下,ck也加了很多索引,包括主鍵索引,分區列,稀疏索引等等。可以看到總體上來講也是有一些提升的,當然也有幾條SQL還是比ck慢,但主體上還是MercsDB比較快的。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

11. 并發查詢和CK性能對比

剛才那個圖是單機的,就是一條SQL一條SQL比的,這裡以20的并發量進行比較,可以看到MercsDB的提升非常明顯。

當然這裡主要是因為MercsDB本身是利用索引進行查詢,意味着它的查詢本身是不需要讀取整個資料的,而ck需要對整個磁盤做掃描,是以時間是線性的或者近線性的增加。

這個是單機和多并發場景下和ck的一個對比。

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

--

04

測試及應用

1. 應用場景:微信支付日志檢索

最後再講幾個應用。

首先是MercsDB在微信支付的日志檢索裡的使用,這裡對寫入要求比較高,而且查詢模式既有點查,也有全局的查詢,這裡是海量資料,剛好MercsDB對寫入海量資料,對查詢模式的支援都比較好,是以就選用了MercsDB。

  • 使用TubeMQ支援了實時寫入。
  • 對資料進行了分詞和索引,既支援了全局查,也支援點查。
  • 對存儲進行了分離,把索引放到了本地,這樣檢索的時候會更快,原始資料放到HDFS。
騰訊新一代多元分析引擎MercsDB在微信支付上的應用

2. 應用場景:廣告業務AB測試

另一個是在AB測試中用到了MercsDB,對于廣告這種AB測,都有廣告主id或廣告id這種主鍵,是以我們做了下面的優化:

  • 對主鍵做了排序。
  • 對其餘列做了壓縮,節省了大量空間。
  • 通過cache+presto的方式支援了ab測試中各種複雜的join。
騰訊新一代多元分析引擎MercsDB在微信支付上的應用

--

05

未來規劃

這是一些應用場景,最後來說一下未來的規劃。

1.開源加上雲,這是未來最重要的一些事情,這也是改名的原因

2.會繼續做一些向量化的優化

3.容錯方面,比如失敗之後的重試,還需要在容錯方面做的更好

4.記憶體管理和可用性方面這些正常的優化,會繼續做下去

--

06

問答環節

Q1:高并發和即時的查詢支援的怎麼樣?

A1:高并發:支援量還是比較大的,QPS在百肯定是沒有問題。

Q2:索引用的lucene還是自己研發的?

A2:最開始用的lucene,但是後面都是我們自己進行的一些改造和研發。

Q3:分詞用的什麼算法?

A3:用的lucene自帶的算法。

DataFunTalk

今天的分享就到這裡,謝謝大家。

分享嘉賓:龍躍 騰訊TEG 技術專家

編輯整理:張建闖 BOSS直聘

出品平台:DataFunTalk

01/分享嘉賓

騰訊新一代多元分析引擎MercsDB在微信支付上的應用

龍躍|騰訊TEG 技術專家

北京大學計算機本碩,多年OLAP從業經驗,聚焦于以Spark, Presto等為代表的OLAP引擎。曾任位元組跳動Presto負責人,現為騰訊TEG資料中心OLAP方向技術專家。

02/關于我們

DataFun:專注于大資料、人工智能技術應用的分享與交流。發起于2017年,在北京、上海、深圳、杭州等城市舉辦超過100+線下和100+線上沙龍、論壇及峰會,已邀請超過2000位專家和學者參與分享。其公衆号 DataFunTalk 累計生産原創文章800+,百萬+閱讀,15萬+精準粉絲。

繼續閱讀