PB 級數(shù)據(jù)即席查詢基于 Flink 的實踐


第一部分 “Threat Hunting 平臺的架構(gòu)與設(shè)計” 將由蘇軍來為大家分享; 第二部分 “以降低 IO 為目標(biāo)的優(yōu)化與探索” 將由劉佳來為大家分享。
一、Threat Hunting 平臺的架構(gòu)與設(shè)計
第一部分內(nèi)容大致分為三個部分,分別是:
平臺的演進(jìn) 架構(gòu)設(shè)計 深入探索索引結(jié)構(gòu)
1. 平臺的演進(jìn)

2017 年我們基于 Flink DataStream 開發(fā)了用戶行為分析系統(tǒng) UEBA,它是通過接入企業(yè) IT 拓?fù)涞母黝愋袨閿?shù)據(jù),比如身份認(rèn)證數(shù)據(jù)、應(yīng)用系統(tǒng)訪問數(shù)據(jù)、終端安全數(shù)據(jù)、網(wǎng)絡(luò)流量解析數(shù)據(jù)等等,以用戶 / 資產(chǎn)為核心來進(jìn)行威脅行為的實時檢測,最后構(gòu)建出用戶威脅等級和畫像的系統(tǒng); 2018 年基于 UEBA 的實施經(jīng)驗,我們發(fā)現(xiàn)安全分析人員往往需要一種手段來獲取安全事件對應(yīng)的原始日志,去進(jìn)一步確認(rèn)安全威脅的源頭和解決方式。于是我們基于 Spark 開發(fā)了 HQL 來解決在離線模式下的數(shù)據(jù)檢索問題,其中 HQL 可以認(rèn)為是表達(dá)能力比 SQL 更加豐富的查詢語言,大致可以看作是在 SQL 能力的基礎(chǔ)上增加了算法類算; 2019 年隨著離線 HQL 在客戶那邊的使用,我們發(fā)現(xiàn)其本身就能夠快速定義安全規(guī)則,構(gòu)建威脅模型,如果在離線模式下寫完語句后直接發(fā)布成在線任務(wù),會大大縮短開發(fā)周期,加上 Flink SQL 能力相對完善,于是我們基于 Flink SQL + CEP 來升級了 HQL 的能力,產(chǎn)生了 HQL RealTime 版本; 2020 年隨著客戶數(shù)據(jù)量的增大,很多已經(jīng)達(dá)到了 PB 級,過往的解決方案導(dǎo)致離線的數(shù)據(jù)檢索性能遠(yuǎn)遠(yuǎn)低于預(yù)期,安全分析人員習(xí)慣使用 like 和全文檢索等模糊匹配操作,造成查詢延時非常大。于是從今年開始,我們著重優(yōu)化 HQL 的離線檢索能力,并推出了全新的 Threat Hunting 平臺。

第一是低成本的云原生架構(gòu)。我們知道目前大部分的大數(shù)據(jù)架構(gòu)都是基于 hadoop 的,其特點(diǎn)是數(shù)據(jù)就在計算節(jié)點(diǎn)上,能夠減少大量網(wǎng)絡(luò)開銷,加速計算性能。但是整個集群為了做到資源均衡,往往需要相同的資源配置,且為了能夠存儲盡量多的數(shù)據(jù),集群規(guī)模會很大, 所以這類架構(gòu)在前期需要投入大量硬件成本。 而存算分離和彈性計算則能夠解決這一問題,因為磁盤的價格是遠(yuǎn)低于內(nèi)存和 CPU 的,所以用廉價的磁盤存儲搭配低配 CPU 和內(nèi)存來存儲數(shù)據(jù),用少量高配機(jī)器來做計算,可以在很大程度上降低成本。 第二是低延時的查詢響應(yīng)。安全分析人員在做威脅檢測時,大部分時間是即席查詢,即通過過濾、join 來做數(shù)據(jù)的檢索和關(guān)聯(lián)。為了能夠盡快的獲取查詢結(jié)果,對應(yīng)的技術(shù)方案是:列存/索引/緩存。 列存不用多說了,是大數(shù)據(jù)領(lǐng)域常見的存儲方案; 在列存的基礎(chǔ)上,高效的索引方案能夠大量降低 io,提高查詢性能; 而存算分析帶來的網(wǎng)絡(luò)延時可以由分布式緩存來彌補(bǔ)。 第三是需要豐富的查詢能力,其中包括單行的 fields/filter/udf 等,多行的聚合 /join,甚至算法類的分析能力,這部分我們主要依賴于自己開發(fā)的分析語言 HQL 來提供。
2. 架構(gòu)設(shè)計

如果分析語句的輸入是已經(jīng)算好并且 cache 好了的中間結(jié)果,那么直接讀取緩存來繼續(xù)計算; 如果不能命中,證明我們必須從 orc 文件開始重新計算。
第一點(diǎn)是索引數(shù)據(jù)庫會返回一批符合該條件的文件列表,如果文件列表非常大的話,當(dāng)前的 Flink 版本在構(gòu)建 job graph 時,在獲取 Filelist Statistics 邏輯這里在遍歷大量文件的時候,會造成長時間無法構(gòu)建出 job graph 的問題。目前我們對其進(jìn)行了修復(fù),后期會貢獻(xiàn)給社區(qū)。 第二點(diǎn)是數(shù)據(jù)緩存那一塊,我們的 HQL 之前是通過 Spark 來實現(xiàn)的。用過 Spark 的人可能知道,Spark 會把一個 table 來做 cache 或 persist。我們在遷移到 Flink 的時候,也沿用了這個算子。Flink 這邊我們自己實現(xiàn)了一套,就是用戶在 cache table 時,我們會把它注冊成一個全新的 table source,后面在重新讀取的時候只會用這個新的 table source 來打通整個流程。
3. 深入探索索引結(jié)構(gòu)


第一是 transaction。我們知道列存文件往往是無法 update 的,而我們在定期優(yōu)化文件分布時會做 Merge File 操作,為了保證查詢一致性,需要數(shù)據(jù)庫提供 transaction 能力。 第二是性能。數(shù)據(jù)庫擁有較強(qiáng)的讀寫和檢索能力,甚至可以將謂詞下推到數(shù)據(jù)庫來完成,數(shù)據(jù)庫的高壓縮比也能進(jìn)一步節(jié)省存儲。



二、以降低 IO 為目標(biāo)的優(yōu)化與探索
上文介紹了為什么要選擇塊索引,那么接下來將具體介紹如何使用塊索引。塊索引的核心可以落在兩個字上:“裁剪”。裁剪就是在查詢語句被真正執(zhí)行前就將無關(guān)的文件給過濾掉,盡可能減少進(jìn)入計算引擎的數(shù)據(jù)量,從數(shù)據(jù)源端進(jìn)行節(jié)流。

第一步是解析查詢語句。獲取到相關(guān)的 filter,可以看到最左邊的 SQL 語句中有兩個過濾條件, 分別是 src_address = 某個 ip,occur_time > 某個時間戳。 第二步將查詢條件帶入 Index DB 對應(yīng)數(shù)據(jù)源的 meta 表中去進(jìn)行文件篩選。src_address 是字符串類型字段,它會聯(lián)合使用 min/max 和 bloom 索引進(jìn)行裁剪。occur_time 是數(shù)值類型字段并且是時間字段,我們會優(yōu)先查找 min/max 索引來進(jìn)行文件裁剪。需要強(qiáng)調(diào)的是, 這里我們是將用戶寫的 filter 封裝成了 index db 的查詢條件,直接將 filter pushdown 到數(shù)據(jù)庫中完成。 第三步在獲取到文件列表后,這些文件加上前面提到的 merged schema 會共同構(gòu)造成一個 TableSource 來交給 Flink 進(jìn)行后續(xù)計算。

第一點(diǎn),數(shù)據(jù)在未排序的情況下,裁剪率是有理論上限的,我們通過在數(shù)據(jù)寫入的時候使用 hilbert 曲線排序原始數(shù)據(jù)來提升裁剪率; 第二點(diǎn),因為安全領(lǐng)域的特殊性,做威脅檢測嚴(yán)重依賴 like 語法,所以我們對 orc api 進(jìn)行了增強(qiáng),使其支持了 like 語法的下推; 第三點(diǎn),同樣是因為使用場景嚴(yán)重依賴 join,所以我們對 join 操作也做了相應(yīng)的優(yōu)化; 第四點(diǎn),我們的系統(tǒng)底層支持多種文件系統(tǒng),所以我們選取 Alluxio 這一成熟的云原生數(shù)據(jù)編排系統(tǒng)來做數(shù)據(jù)緩存,提高數(shù)據(jù)的訪問局部性。
1. 裁剪率的理論上限及 Hilbert 空間填充曲線

第一點(diǎn),如果命中總行數(shù) = 總塊數(shù),即 X 軸值為 1 的時候,命中率為 2/3, 也就是 2/3 的塊,都包含命中的行,對應(yīng)的塊修剪率的上限是 1/ 3。1/3 是一個很低數(shù)值,但是由于它的前提是數(shù)據(jù)隨機(jī)均勻分布,所以為了讓數(shù)據(jù)分布更好,我們需要在數(shù)據(jù)寫入時對原始數(shù)據(jù)進(jìn)行排序。 第二點(diǎn),假設(shè)命中總行數(shù)固定,那么大幅度減少每塊中的行數(shù)來增加總塊數(shù),也能提升塊修剪率。所以我們縮小了塊大小。根據(jù)測試結(jié)果,我們設(shè)定每個文件的大小為:16M??s小文件大小是很簡單的。針對排序,我們引入了 hilbert 空間填充曲線。

首先是,以什么路徑遍歷 2 維空間,使路徑的地址序列對其中任一維度都基本有序?為什么要對每一列或者說子集都有序?因為系統(tǒng)在使用的過程中,查詢條件是不固定的。數(shù)據(jù)寫入時排序用到了 5 個字段,查詢的時候可能只用到了其中的一個或兩個字段。Hilbert 排序能讓多個字段做到既整體有序,又局部有序。 另外,空間填充曲線有很多,還有 Z 形曲線、蛇形曲線等等,大家可以看看右邊這兩張對比圖。直觀的看,曲線路徑的長跨度跳躍越少越好,點(diǎn)的位置在迭代過程中越穩(wěn)定越好。而 hilbert 曲線在空間填充曲線里面綜合表現(xiàn)最好。

2. 字典索引上 Like 的優(yōu)化

首先我們?yōu)?ORC api 添加了 like 條件表達(dá)式,保證 SQL 中的 like 能下推到 orc record reader 中。 其次,重構(gòu)了 orc record reader 的 row group filter 邏輯,如果發(fā)現(xiàn)是 like 表達(dá)式,首先讀取該字段的 dict steam,判斷 dict stream 是否包含 like 目標(biāo)字符串,如果字典中不存在該值,直接跳過該 row group,不用讀取 data stream 和 length steam,能大幅提高文件讀取速度。后期我們也考慮構(gòu)建字典索引到索引數(shù)據(jù)庫中,直接將字典過濾 pushdown 到數(shù)據(jù)庫中完成。
3. 基于索引對 join 的優(yōu)化


4. Alluxio 作為對象存儲的緩存


三、未來規(guī)劃
評論
圖片
表情

