更快更強(qiáng),SLS 推出高性能 SPL 日志查詢模式
引言
隨著數(shù)字化進(jìn)程的持續(xù)深化,可觀測性一直是近年來非?;馃岬脑掝},在可觀測的三大支柱 Log/Trace/Metric 中,日志(Log)數(shù)據(jù)一般是最為常見的,企業(yè)邁向可觀測性的第一步,也往往始于日志數(shù)據(jù)的采集上云。日志完成收集后,最直接的需求就是從海量日志數(shù)據(jù)中檢索分析出有價值的信息。隨著日志數(shù)據(jù)量的不斷增長,數(shù)據(jù)種類不斷增多并日益朝著非結(jié)構(gòu)化、多場景、多模態(tài)等方向演進(jìn),傳統(tǒng)的日志搜索方式已經(jīng)越來越難以滿足不同場景下多樣化、個性化的分析需求。
日志數(shù)據(jù)的查詢分析需求是多樣化的
日志(Log)數(shù)據(jù)作為可觀測場景中最基礎(chǔ)的數(shù)據(jù)類型之一,具備以下特點 :
- 不可變:日志數(shù)據(jù)一旦產(chǎn)生就不會被再次修改,是對事件原始信息的忠實記錄,往往結(jié)構(gòu)不太固定。
- 數(shù)據(jù)隨機(jī):比如異常事件日志、用戶行為日志,一般天然就是隨機(jī)的、難以預(yù)測的。
- 來源多樣:日志數(shù)據(jù)種類繁多,不同來源的數(shù)據(jù)難以具有統(tǒng)一的 Schema。
- 業(yè)務(wù)復(fù)雜:不同的業(yè)務(wù)參與方對數(shù)據(jù)的理解不同,寫日志過程中難以預(yù)見到后期具體的分析需求。
這些因素導(dǎo)致日志數(shù)據(jù)在采集過程中往往并不存在一個理想的數(shù)據(jù)模型可以用來預(yù)處理日志數(shù)據(jù),因此更常見的做法是直接采集存儲原始的日志數(shù)據(jù),這可以稱為是一種 Schema-on-Read 的模式,或者是所謂的壽司原則(The Sushi Principle:Raw data is better than cooked, since you can cook it in as many different ways as you like)。
這種直接存儲原始數(shù)據(jù)的做法,意味著在分析的時候往往需要動態(tài)實時的對數(shù)據(jù)進(jìn)行處理(比如 json 處理、正則提取、數(shù)學(xué)計算等等);而且由于不同分析人員對數(shù)據(jù)特征缺乏先驗知識,一般也需要對數(shù)據(jù)先進(jìn)行一定的探索式分析。
也就是說,在日志查詢分析的過程,既需要能夠展現(xiàn)非結(jié)構(gòu)化的文檔結(jié)構(gòu),又需要豐富的算子進(jìn)行實時處理,同時最好還能夠便捷的支持級聯(lián)式、探索式的分析方式。
理想的日志查詢語法應(yīng)該是什么樣的
日志數(shù)據(jù)分析通??梢苑譃閮纱箢悎鼍埃?/span>
一類是查詢類場景,或者說是搜索場景、純過濾場景,即按照特定的條件過濾掉不需要的日志,并針對符合條件的日志直接輸出日志原文。
一類是分析類場景,主要包括聚合分析(比如 sum、sort)、關(guān)聯(lián)分析(比如多個表 join),需要對數(shù)據(jù)進(jìn)行更復(fù)雜的計算,輸出結(jié)果一般是表格模式。
這里我們重點關(guān)注純查詢過濾的場景,在 SLS 中既可以用傳統(tǒng)的搜索語法(如 Key:XXX ),也可以在標(biāo)準(zhǔn) SQL 中使用 where 語句(如 * | select * where Key like '%XXX%'),兩種方式各有優(yōu)點,卻也都有著各自的局限性。
對于查詢語法來說,天然就是為過濾搜索場景而生的,但是可惜表達(dá)能力有限,只能支持關(guān)鍵詞匹配,以及多個條件的 And/Not/Or 的邏輯組合,無法支持更為復(fù)雜的處理邏輯。
而對于 SQL 語法來說,優(yōu)點是表達(dá)能力強(qiáng),但 SQL 是表格模型,不便于查看原始日志結(jié)果(因為要將字段對齊,輸出結(jié)果中對于不存在的列就會填充大量 null),而且對于 select * 這樣的語句,也只能輸出開啟字段索引的字段。
詳細(xì)對比如下:
純查詢場景的挑戰(zhàn) | 搜索查詢語法 | 標(biāo)準(zhǔn)SQL語法 |
需要復(fù)雜的處理邏輯 | 弱,主要就是支持關(guān)鍵詞匹配 | 強(qiáng),具備豐富的處理函數(shù)和算子,如正則匹配、json提取 |
輸出內(nèi)容是非結(jié)構(gòu)化的 | 強(qiáng),輸出的是原文,便于查看 | 弱,輸出的是表格模式,不存在的字段全部要補(bǔ)空值,不利于查看 |
翻頁邏輯 | 簡單,控制臺可以直接點,API傳遞offset+lines即可 | 較復(fù)雜,需要在SQL中通過limit x,y的方式,并且要指定排序方式 |
查看結(jié)果的時間分布 | 簡單,histogram柱狀圖直觀展示出不同時間的分布 | 較復(fù)雜,需要在SQL中按照時間分組求和,再按時間排序,然后再畫線圖查看 |
結(jié)果中輸出所有原文字段 | 輸出的是原文,天然包含所有字段 | 較麻煩,select * 只能輸出建了字段索引的列 |
獲取部分字段 | 不支持 | select指定列即可 |
計算出新的列 | 不支持 | select中可以計算新的列 |
多級級聯(lián)處理能力 | 無法表達(dá) | 可以通過with語句、SQL嵌套,但寫起來較為復(fù)雜 |
既然兩種方式各有所長,那么我們是否可以結(jié)合這兩種方式的優(yōu)點,支持一種新的查詢語法,既能遵從文檔模型(直接輸出日志原文、不按表格模式、不要求所有輸出列有索引),又能支持各種好用的 SQL 算子,同時還能夠支持一種更便捷的級聯(lián)處理(而不需要復(fù)雜的多層嵌套)呢?
SPL 管道式查詢語言
SPL 基本語法如下:
<data-source> | <spl-expr> ... | <spl-expr> ...
其中 <data-source> 是數(shù)據(jù)源,對于日志查詢的場景,指的就是索引查詢語句。<spl-expr> 是 SPL 指令,支持正則取值、字段分裂、字段投影、數(shù)值計算等多種豐富的操作,具體參考 SPL 指令介紹[2]。
從語法定義上可以看到,SPL 是支持多個 SPL 指令組成管道級聯(lián)的。對于日志查詢的場景來說,在索引查詢語句之后,可以根據(jù)需要通過管道符不斷追加 SPL 指令,從而獲得類似 Unix 管道處理文本數(shù)據(jù)的體驗,對日志進(jìn)行靈活的探索式分析。
SPL 能做什么?
篩選字段獲得更精確的視圖
在查詢?nèi)罩镜臅r候,往往是帶著某個目的去檢索,這個時候一般是只關(guān)心其中的部分字段。這時就可以使用 SPL 中的 project 指令,只保留自己關(guān)心的字段。(或者使用 project-away 指令,移除不需要看到的字段)
實時計算出新的字段
使用 Extend 指令,可以基于已有字段加工提取出新的字段,可以使用豐富的函數(shù)(這些大部分是和 SQL 語法通用的)進(jìn)行標(biāo)量處理。
Status:200 | extend urlParam=split_part(Uri, '/', 3)
同時也可以根據(jù)多個字段計算出新的字段,比如計算兩個數(shù)字字段的差值。(注意字段默認(rèn)是被視為 varchar,進(jìn)行數(shù)字類型計算的時候要先通過 cast 轉(zhuǎn)換類型)
Status:200 | extend timeRange = cast(BeginTime as bigint) - cast(EndTime as bigint)
并且也可以在后續(xù)管道中,再對這個計算后的值進(jìn)行 where 判斷過濾:
Status:200
| where UserAgent like '%Chrome%'
| extend timeRange = cast(BeginTime as bigint) - cast(EndTime as bigint)
| where timeRange > 86400
自由的展開半結(jié)構(gòu)化數(shù)據(jù)
SPL 提供了 parse-json、parse-csv 這樣的指令,可以將 json、csv 類型的字段,直接完全展開出為獨立的字段,之后就可以直接對這些字段進(jìn)行操作。省去了書寫字段提取函數(shù)的開銷,在交互式查詢場景中這種寫法是更為便捷的。
SPL 之前已經(jīng)在掃描查詢模式上全地域支持,詳見掃描查詢[3]。掃描查詢可以不依賴索引,直接掃描原始日志數(shù)據(jù)計算。下圖中這個例子,就是在原始日志數(shù)據(jù)上,通過 SPL 管道完成了模糊過濾、json 展開、字段提取等多種操作。
當(dāng)前掃描模式 SPL 難以處理大規(guī)模數(shù)據(jù)
掃描模式具備很好的靈活性,但最大的問題是性能不足,特別是面對大規(guī)模數(shù)據(jù)時難以在有限時間內(nèi)處理完?,F(xiàn)有的掃描查詢限制單次最多掃描 10 萬行,超出限制后需要控制臺手動點擊觸發(fā)下一次掃描(或者 SDK 觸發(fā)下一次調(diào)用)。
由于性能受限,導(dǎo)致現(xiàn)有的 SPL 查詢在使用上存在以下問題:
- 對于過濾結(jié)果較為稀疏的查詢,由于單次掃描的原始數(shù)據(jù)量太少,很難在有限時間內(nèi)掃描到結(jié)果。
- 查詢界面的直方圖展示的是索引過濾后的結(jié)果(以及掃描進(jìn)度),而無法展示出 SPL 條件過濾后的最終結(jié)果分布。
- 無法支持針對最終過濾后的結(jié)果隨機(jī)翻頁,只能按照已經(jīng)掃描的原文的 offset 進(jìn)行連續(xù)翻頁掃描。
這些約束,導(dǎo)致掃描模式下的 SPL,面對具備較大規(guī)模的日志數(shù)據(jù),使用體驗較差,也就很難發(fā)揮出實際用處。
極致優(yōu)化,高性能 SPL 模式
計算下推,并行化加速
首先要在架構(gòu)上解決水平擴(kuò)展的問題。原有的架構(gòu)下,因為存儲節(jié)點不具備復(fù)雜表達(dá)式的計算能力,只能將原始數(shù)據(jù)全量拉取到計算節(jié)點處理,大數(shù)據(jù)量的讀取、傳輸、序列化是很大的瓶頸。
在查詢場景下,實際單次請求每次需要的最終結(jié)果行數(shù)是比較少的(一般單次請求 100 行以內(nèi),超出后通過翻頁請求獲取),關(guān)鍵在于當(dāng) SPL 語句中包含 where 條件的時候,就存在從大量數(shù)據(jù)中計算 where 條件過濾的過程。為了能夠處理大規(guī)模數(shù)據(jù)并減少傳輸開銷,我們就需要將 where 條件的計算下推到各個 shard 所在的存儲節(jié)點上處理。相應(yīng)的,也就必須要求存儲節(jié)點具備對 SPL 中豐富算子的高效處理能力。
為此我們在存儲節(jié)點上,引入 C++ 向量化計算引擎,在存儲節(jié)點上讀取了原始的數(shù)據(jù)后,直接原地就可以進(jìn)行高效的過濾計算。只有對滿足 where 條件的日志,才需要進(jìn)行剩余的 SPL 計算并輸出最終結(jié)果。
計算下推之后,整個的處理能力就可以隨著 shard 數(shù)目水平擴(kuò)展,同時也大幅減少了存儲節(jié)點和計算節(jié)點之間的數(shù)據(jù)傳輸、網(wǎng)絡(luò)序列化開銷。
向量化計算,多級火箭加速
計算下推解決了按 shard 水平擴(kuò)展的問題,接下來我們還要進(jìn)一步的大幅提升每個 shard 上的處理能力。
掃描模式的 SPL,最大性能瓶頸還是在于直接掃描讀取原始的行數(shù)據(jù)。這樣讀放大會比較嚴(yán)重,IO 效率很低。正如使用 SQL 分析能力時需要開啟字段索引(并開啟統(tǒng)計),這些字段的數(shù)據(jù)就可以被高效的讀取和計算,那 SPL 同樣也可以基于字段索引來進(jìn)行高性能的數(shù)據(jù) IO,然后再基于 SIMD 向量化技術(shù)進(jìn)行高性能計算,同時在過程中盡可能減少額外計算量。
以圖中的 SPL 為例,在下推到存儲節(jié)點后,會經(jīng)過“多級火箭”進(jìn)行層層加速:
- 按照查詢時間范圍過濾(當(dāng)數(shù)據(jù)量非常大時,建議選擇必要的時間范圍進(jìn)行分析)。
- 處理第一級管道 Status:200 ,關(guān)鍵詞索引條件過濾(這個是最快的,有索引過濾條件盡量寫上過濾條件)。
- 處理 SPL 中的 where 過濾條件,基于字段索引(并開啟統(tǒng)計),高效讀取對應(yīng)的數(shù)據(jù)。
- 向量化高性能計算,獲得過濾結(jié)果,然后再計算剩余的 SPL 部分,得到最終結(jié)果
- 同時在計算過程中,如果發(fā)現(xiàn)過濾結(jié)果行數(shù)已經(jīng)滿足要求,則盡量提前終止(特別對于高命中率的情形,可以盡量減少不必要的計算)。
經(jīng)過這些優(yōu)化之后,高性能 SPL 的執(zhí)行性能相比掃描模式,得到了質(zhì)的飛躍。
高性能 SPL 的性能表現(xiàn)
我們以單個 shard 處理 1 億行數(shù)據(jù)為例,來評估高性能 SPL 的性能表現(xiàn)。在線上真實環(huán)境創(chuàng)建一個 Logstore,10 個 shard,查詢時間范圍內(nèi)有 10 億數(shù)據(jù)。(服務(wù)訪問日志數(shù)據(jù))
選取如下幾個典型的場景:
場景 1:通過字符串函數(shù)處理后過濾
SPL 語句:* | where split_part(Uri, '#', 2) = 'XXX'
場景 2:短語查詢、模糊查詢
SPL 語句:* | where Content like '%XXX%'
場景 3:json提取子字段,然后再過濾
SPL 語句: * | where json_extract_scalar(Params, 'Schema') = 'XXX'
在上述語句中選擇不同的比較參數(shù),構(gòu)造出不同的命中率的場景(比如命中率 1%,指的是原始 10 億條數(shù)據(jù)中,有 1000 萬條滿足 where 條件的結(jié)果數(shù)據(jù)),并請求前 20 條滿足條件數(shù)據(jù)(對應(yīng) GetLogs 接口的 API 參數(shù)是 offset=0, lines=20),測試平均耗時。
命中率 | 場景1 耗時 | 場景2 耗時 | 場景3 耗時 |
1% | 52 ms | 73 ms | 89 ms |
0.1% | 65 ms | 94 ms | 126 ms |
0.01% | 160 ms | 206 ms | 586 ms |
0.001% | 1301 ms | 2185 ms | 3074 ms |
0.0001% | 2826 ms | 3963 ms | 6783 ms |
可以看出:
- 當(dāng)命中率較高時,不同場景下都有很好的性能表現(xiàn),甚至可以接近關(guān)鍵詞索引查詢。
- 當(dāng)命中率很低時,由于要實時計算大量數(shù)據(jù),需要更長一些的執(zhí)行時間,具體實際性能表現(xiàn)和數(shù)據(jù)字段的長度、語句中算子復(fù)雜度、命中結(jié)果在原始數(shù)據(jù)的分布位置等因素都有關(guān)。
- 整體來看,高性能 SPL 對于數(shù)十億級別的日志量級,可以在數(shù)秒內(nèi)完成計算。
控制臺交互升級,展示過濾后結(jié)果的直方圖
高性能模式 SPL,由于計算性能有了大幅提升,因此控制臺展示 histogram,直接展示的是整個 SPL 語句過濾后的結(jié)果分布。(意味著整個范圍內(nèi)的數(shù)據(jù)也進(jìn)行了全量的計算)
舉個例子,原始日志有 1000 萬條,SPL 語句是 Status:200 | where Category like '%xx%',符合 Status:200 條件的日志是 10 萬條,這其中再符合 where Category like '%xx%' 條件的日志是 1000 條,則查詢界面上 histogram 柱狀圖展示的是這最終的 1000 條日志隨時間的分布情況。
相應(yīng)的,和純索引查詢模式下的交互完全相同,高性能模式 SPL 支持隨機(jī)翻頁,也支持點擊柱狀圖直接跳轉(zhuǎn)到對應(yīng)區(qū)間的查詢結(jié)果。
API 調(diào)用簡化,統(tǒng)一的 offset 語義
在高性能 SPL 模式下,調(diào)用 GetLogs 通過 SPL 語句查詢?nèi)罩緯r,offset 直接表示的就是過濾后的結(jié)果偏移量,從而大大簡化了 API 調(diào)用方式。也就是說,使用上,和純索引查詢完全統(tǒng)一。直接按照過濾后最終結(jié)果的 offset 來翻頁即可。
使用說明
如何開啟高性能 SPL?
無須顯式指定運行模式。當(dāng) SPL 語句中所有參與 where 條件計算的列,全都已經(jīng)創(chuàng)建了字段索引(并開啟了統(tǒng)計),則自動按照高性能模式執(zhí)行;否則以掃描模式執(zhí)行。
是否計費?
高性能 SPL 模式,查詢本身不產(chǎn)生任何額外費用。
?? 注意:如果沒有完全命中索引列導(dǎo)致走的是掃描模式 SPL(并且當(dāng)前 Logstore 是按功能計費模式),則按照查詢過程中的掃描原始日志數(shù)據(jù)量計費。
最佳實踐
盡可能增加索引查詢語句預(yù)過濾
如果有關(guān)鍵詞索引過濾條件,盡可能使用,放在多級 SPL 管道的第一級。索引查詢的效率總是最高的。
復(fù)雜過濾場景,建議使用 SPL 代替 SQL
特別是對于模糊匹配、短語匹配、正則匹配、json 提取以及更復(fù)雜的各種純過濾場景,以前只能使用 SQL 語法(* | select * where XXX),現(xiàn)在建議替換為 SPL 語法(* | where XXX)??梢阅芨玫妮敵鋈罩驹模ǘ皇潜砀衲J剑憬莸目吹竭^濾后的結(jié)果柱狀圖分布,以及更簡潔的輸入體驗。
更多功能,敬請期待
SPL 也能支持聚合操作
目前 SPL 僅支持純查詢過濾場景下的使用,接下來在日志查詢場景下,SPL 語法會進(jìn)一步支持排序、聚合等操作(聚合后按照表格模式輸出),從而使得 SPL 的多級管道級聯(lián)處理能力更強(qiáng)大、更完善,能夠更好的對日志進(jìn)行更靈活的查詢分析。
總結(jié)
企業(yè)的日志數(shù)據(jù)上云后,從海量日志中搜索出想要的信息,是一項最基本的需求。SLS 推出 SPL 查詢語法,支持類似 Unix 管道的級聯(lián)語法,并支持 SQL 的各種豐富的函數(shù)。同時,基于計算下推、向量化計算等優(yōu)化,支持高性能模式 SPL 查詢,可以在數(shù)秒內(nèi)處理億級數(shù)據(jù),并且支持 SPL 過濾后最終結(jié)果的分布直方圖、隨機(jī)翻頁等特性,具備和純索引查詢模式類似的體驗。對于模糊、短語、正則、json 提取以及各種復(fù)雜過濾場景,推薦使用 SPL 語句來進(jìn)行查詢。
高性能模式 SPL 目前正在按區(qū)域逐步發(fā)布中,有任何使用上的問題或者需求,可以通過工單或者直接在 SLS 的釘釘群咨詢。SLS 會一直持續(xù)不斷的優(yōu)化,提供更強(qiáng)大、更好用的可觀測存儲分析引擎。