Presto 在騰訊資訊業務中的應用
團隊:騰訊醫療資訊與服務部 - 技術研發中心
前言:隨着產品矩陣和團隊規模的擴張,跨業務、APP 的數據處理、分析總是不可避免。一個顯而易見的問題就是異構數據源的連通。我們基於 PrestoDB 構建了業務線內適應騰訊生態的聯邦查詢引擎,連通了部門內部 20 + 數據源實例,涵蓋了 90% 的查詢場景。同時,我們參與公司級的 Presto Oteam 進行協同共建,在引擎層面做了諸多改造。在實際使用 Presto 的過程中,也發現其 SQL 表達能力的過人之處。本文將從 Presto 使用者和開發者兩種角度,給大家分享一些技術落地過程中的乾貨。
- 簡介 =====
Presto 是 facebook 研發的基於 SQL 進行大數據分析的高性能分佈式計算引擎,最開始是用來解決 Hive 速度慢以及異構數據源互通的問題。在大數據家族中屬於 MPP(Massive Parallel Processing)計算引擎範疇,其原理是火山(Volcano)模型:將 SQL 抽象成一個個算子(Operator),形成管線(Pipeline)。目前能夠支持 Hive、HBase、ES、Kudu、Kafka、MySQL、Redis 等幾十種數據源的讀取。它有如下特點:
-
基於 SQL 語言,上手成本低,而且功能強大,支持 reduce 和 lambda 函數
-
純計算引擎,解耦底層存儲,可快速縮擴容
-
純內存計算,速度快,提供交互式的查詢體驗
-
通過插件的方式實現拓展功能,二次開發友好
-
通過不同的連接器(Connector)插件讀取異構數據源,進行聯邦查詢
1.Presto 架構圖
- 業務現狀 =======
無論是傳統信息流業務,或是醫療業務,或多或少都會遇到異構數據源整合問題。比如醫生、患者的狀態數據,由後臺維護,前端上報數據則在 Hive 中。另外,由於相同數據源不同版本間差異較大,往往沒有完整的解決方案,導致查詢分析速度慢,業務叫苦不迭,e.g. Hive 不同實例僅通過 MR 引擎進行互通。
2.1 業務構成
目前,個人接觸過的業務包括資訊類的騰訊看點、騰訊醫典,以及醫生問診相關的騰訊雲醫。
- 業務構成
2.2 痛點問題
數據互通的時候,底層的數據源可能是同一數據源的多個實例,或是不同版本、魔改版本,e.g. 司內 tHive 與 Venus 都是 Hive 數據源。在跨業務 / APP 分析時,這種問題會更加明顯。同時,由於應用場景的不同(離線計算、快速索引),天然也會存在多數據源問題。原因總結如下:
-
團隊技術棧差異
-
同類產品較多
-
架構、歷史遺留
-
應用場景不同
- 異構數據源問題
2.3 主要工作
針對 Hive 查詢提速的問題,我們在聯邦查詢引擎中適配了內部的 Hive 數據源,並且參與中臺 Oteam 項目進行 Hive 兼容、Presto 引擎層優化、改造。同時,我們進行了技術運營工作來幫助大家更好地使用 Presto。針對異構數據源打通的問題,我們進行了聯邦查詢引擎的調研與開發,在引擎層面對內部不同種類的數據源進行適配。最後是一些技術輸出的規劃工作。
-
Presto 技術運營
-
聯邦查詢引擎改造適配
-
Presto Oteam 引擎研發
-
技術輸出
-
技術運營 =======
由於身處業務的數據團隊中,除了參與中臺的技術研發,平時也會使用 Presto,並且負責 SQL 相關問題的答疑,既是開發者,也是使用者。大多數人對 Presto 的印象,僅僅停留在 “都是 SQL 引擎” 上,其實不然。Presto 的 SQL 語言能力非常出色。如 slogan 說宣傳的那樣,SQL on Everything
:不僅能夠連接各種數據源,還能滿足複雜的處理邏輯。如果認爲 “Presto 在 SQL 層面上做到兼容 Hive 就差不多了”,那就沒有真正發揮出 Presto 的威力。
3.1 Reduce + Lambda
以下來自一個真實案例,數據分析同學根據 APP 上報的用戶行爲日誌進行清理、建模。
-
v1 版本:對用戶路徑按時間排序,然後輸入模型進行建模。通過以下 SQL 片段可以滿足需求。首先用 array_agg 將用戶所有行爲按照 event_time 排序,收集成數組,然後用'/'連接符進行拼接。
-
array_join(array_agg(data order by event_time asc), '/')
-
v2 版本: 由於上報時機的原因,總是會有些相鄰的重複上報,分析同學希望把這些相鄰數據剔除掉,例如有些行爲定時 10s 上報一次,期望達到如下的效果:
-
A/B/A/A/C -> A/B/A/C
如果不是別人問,自己是不會想到可以用 SQL 來完成這種操作的。數組相鄰元素去重,乍看是非常特化的需求,SQL 不太可能滿足,但後來發現還真的可以實現。不得不說 Presto 的 reduce 函數,加上自由度極高的 lambda 表達式,以及可以承載多個變量的Row
類型,使得我們幾乎可以在 SQL 中 “編程”(這裏使用針對 array 類型的 reduce 函數,更通用的聚合函數爲 reduce_agg)。最終解法如下:
-- 邏輯:6/4/6/6/10/20 -> 6/4/6/10/20
-- distinct adjacent elements
SELECT reduce(
ARRAY ['6', '4', '6', '6', '10', '20'], -- 輸入
CAST(
ROW(ARRAY[], '')
AS ROW(arr ARRAY(VARCHAR), prev_ele VARCHAR)
), -- 初始狀態S
(S, T) -> CAST(
ROW(IF(S.prev_ele=T, S.arr, S.arr||T), T)
AS ROW(arr ARRAY(VARCHAR), prev_ele VARCHAR)
), -- lambda輸入函數I
S -> array_join(S.arr, '/') -- lambda輸出函數O
);
以作用對象爲數組的 reduce 函數爲例,包含以下 4 個參數:
-
長度爲 N 的數組。每個元素將會依次送入 lambda 輸入函數
-
初始狀態。第一個元素和該狀態作爲 lambda 輸入函數第一次調用的參數
-
一個 lambda 輸入函數。調用 N 次。它接收一個狀態和一個元素,產生一個新的狀態
-
一個 lambda 輸出函數。調用一次。對 3 中處理完的最終狀態做一次變換
reduce(array(T), initialState S, inputFunction(S, T, S), outputFunction(S, R)) → R
可以看到,示例中的狀態 S 是一個Row
類型的變量,它可以存儲多個元素。第一個是去重數組 arr,第二個是上一個元素的值 prev_ele。lambda 輸入函數每次接收到一個新的值,和 prev_ele 比較,相等則什麼也也不做,不等則將新值放入去重數組中,同時更新 prev_ele。reduce 是一種通用的模型,lambda 則最大程度地利用了 SQL 的現有能力,使得 Presto 的 SQL 表現力更加強大。
3.2 窗口函數
Presto 中的聚合函數都可以被用在窗口函數中,使用 array_agg 可以把當前的窗口截取下來,結合 Window Frame 可以操縱窗口大小,衍生出很多窗口類型。主要由兩個維度組成:
首先是相同行的處理方式,記爲 dim1:
-
RANGE: 當前窗口會包含值相同的相鄰行
-
ROWS: 當前窗口不會包含值相同的相鄰行
然後是窗口的邊界指定,最後兩種僅支持與 ROWS 連用,記爲 dim2:
-
UNBOUND PRECEDING: 排序後第一個元素
-
UNBOUND FOLLOWING: 排序後最後一個元素
-
N PRECEDING: 排序後,當前行的前 N 行
-
N FOLLOWING: 排序後,當前行的後 N 行
- window frame[1]
通過以下 SQL 的結果,應該能對窗口函數有更進一步的認識。爲了簡化我們假設只有一個 partition,排序爲 asc。列名取值如下所示方便大家理解:
- 命名方式
-- value爲關心的值
-- 以index進行排序
WITH
t1 (value, index) AS
(
SELECT * FROM (VALUES ('a', 1),
('b', 2),
('c', 3),
('d', 4),
('e', 4),
('f', 5),
('g', 5),
('h', 6))
)
SELECT *,
-- 默認
array_agg(value) OVER
(ORDER BY index) res,
-- [開頭, 當前值]
array_agg(value) OVER
(ORDER BY index RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) res_range_uc,
-- [開頭, 當前行]
array_agg(value) OVER
(ORDER BY index ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) res_rows_uc,
-- [當前值, 末尾]
array_agg(value) OVER
(ORDER BY index RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) res_range_cu,
-- [當前行, 末尾]
array_agg(value) OVER
(ORDER BY index ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) res_rows_cu,
-- [前1個值,後1個值] 不支持
-- array_agg(value) OVER (ORDER BY index RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING) res_range_11, not support
-- [前1行,後1行]
array_agg(value) OVER
(ORDER BY index ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) res_rows_11
FROM t1;
presto>
value | index | res | res_range_uc | res_rows_uc | res_range_cu | res_rows_cu | res_rows_11
-------+-------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+-------------
a | 1 | [a] | [a] | [a] | [a, b, c, d, e, f, g, h] | [a, b, c, d, e, f, g, h] | [a, b]
b | 2 | [a, b] | [a, b] | [a, b] | [b, c, d, e, f, g, h] | [b, c, d, e, f, g, h] | [a, b, c]
c | 3 | [a, b, c] | [a, b, c] | [a, b, c] | [c, d, e, f, g, h] | [c, d, e, f, g, h] | [b, c, d]
d | 4 | [a, b, c, d, e] | [a, b, c, d, e] | [a, b, c, d] | [d, e, f, g, h] | [d, e, f, g, h] | [c, d, e]
e | 4 | [a, b, c, d, e] | [a, b, c, d, e] | [a, b, c, d, e] | [d, e, f, g, h] | [e, f, g, h] | [d, e, f]
f | 5 | [a, b, c, d, e, f, g] | [a, b, c, d, e, f, g] | [a, b, c, d, e, f] | [f, g, h] | [f, g, h] | [e, f, g]
g | 5 | [a, b, c, d, e, f, g] | [a, b, c, d, e, f, g] | [a, b, c, d, e, f, g] | [f, g, h] | [g, h] | [f, g, h]
h | 6 | [a, b, c, d, e, f, g, h] | [a, b, c, d, e, f, g, h] | [a, b, c, d, e, f, g, h] | [h] | [h] | [g, h]
(8 rows)
3.3 高階運營
一般來說,通過官方文檔就可以解答大部分問題。但有時候文檔也沒說明的細節,只能看源碼了。關於語法特點的問題,需要查看SqlBase.g4
。比如以下 SQL 爲什麼可以運行?不是所有查詢語句都需要 select 開頭:
presto> (VALUES ('a', 1),('b', 2));
_col0 | _col1
-------+-------
a | 1
b | 2
(2 rows)
語義分析中的問題,需要查看StatementAnalyzer
。比如窗口函數執行完成後,用標量函數做一些加工處理,必須寫在整個窗口函數func2(func1() over ())
的外面,而不是func2(func1()) over ()
。
--報錯
array_join(array_agg(concat(col1, col2)), '/')
over (partition by user_id order by event_time)
vs
--成功
array_join(
array_agg(concat(col1, col2))
over (partition by user_id order by event_time),
'/')
3.4 語法、語義錯誤
還有個問題,到底怎麼區分語法、語義錯誤?對於使用者而言,不建議瞭解。對於開發者來說,還是很有必要了解的。語法錯誤是指通過簡單規則捕獲的 SQL 錯誤,在 Antlr 層面就可以截獲,跟上下文關係不大,e.g. select * from from table1; 語義錯誤需要上下文信息,比如庫表、字段是否合法?對於 Presto 而言,lambda 表達式出現的位置是否合法?瞭解語法、語義的區別,對問題的排查也是十分高效的。
- 聯邦查詢引擎 =========
異構數據源導致的問題:
-
搭建各種 ETL Pipeline,維護成本高
-
數據分析速度嚴重拖慢
爲此,我們引入 Presto 作爲聯邦查詢引擎,一方面利用多數據源能力,減少 ETL 相關工作量。另一方面,利用 Presto 的速度爲業務分析提速。本次介紹兩個數據源適配的工作:
-
爲了適配內部的 tHive,我們在 MetaStore 的 Thrift RPC 協議中植入了內部鑑權機制。
-
針對雲上 ES 的網絡情況,禁用了自動嗅探邏輯。
4.1 tHive 連接器適配
Presto 的 Hive 連接器通過與 HMS(Hive MetaStore)通信獲取 Hive 庫表的位置信息,然後拉取數據。騰訊 tHive 有自己的一套鑑權體系 TAUTH,我們需要將這種鑑權機制引入到 Hive 連接器中。外部一般通過 Thrift RPC 協議與 HMS 通信。那麼如何加入鑑權能力呢?
- 獲取 Hive 庫表元數據
參考 Hive 連接器中Kerberos
機制的實現(下圖),可以看到 rawTransport 作爲參數,用來構建一個新的 SaslTransport。
- KerberosHiveMetastoreAuthentication
結合TSaslClientTransport
的源碼可以發現,這裏其實是計算機網絡分層思想的典型應用。在可靠傳輸層 rawTransport 的基礎上,再包裝了一個 Sasl 層。利用底層 rawTransport 提供的可靠傳輸能力,進一步提供安全策略。e.g. 某些 QoS 條件下,調用 Sasl 層的write()
,會對數據進行加密,Sasl 進而調用下一層的write()
函數,將加密後的數據發送到可靠的傳輸通道中。它們都實現了TTtransport
接口,I/O 函數如下所示:
-
open()
-
close()
-
flush()
-
readAll()
-
write()
- 本質爲網絡協議棧
Sasl 層本身並不綁定特定的鑑權機制,它是一個框架。通過 JCA 註冊的鑑權機制都可以在運行時被指定。
- 鑑權機制插件化
所以如果想整合自定義的鑑權機制,需要註冊對應的SecurityProvider
。
- 底層原理
總結:對於小白來說,“爲 Hive 連接器增加一種鑑權機制” 是個很難理解的技術需求,通過前文的探索,我們發現其本質是:“如何在 HMS 的 Thrift RPC 中,爲 SASL 鑑權層增加一種自定義的安全協議。” 這裏的上下文比較多,需要對 HMS、THrift RPC、SASL、JCA、Kerberos 等概念有個大概的瞭解,才知道需要做什麼。對技術的提升還是很有幫助的。
4.2 ES 連接器踩坑
第二個 case:調研 ES 連接器的時候,發現 Presto 啓動時第一次連接 ES 集羣是成功的。但是後面哪怕沒有執行 ES 相關查詢也會無故報錯,堆棧信息顯示網絡連接失敗。
- 報錯信息
經過排查,發現與定時嗅探邏輯有關。Presto 底層依賴了 facebook 內部的Airlift
後臺框架。在這個場景下,通過Bootstrap
註冊的類會被生命週期管理器識別,@PostConstruct
註解(Annotation)標記的函數會在類實例化後被自動調用。可以看到,一個refreshNodes()
函數被定期調用了,該函數會獲取 ES 集羣中所有的可用節點 IP,並在下次將請求發送到其中一個節點。
- @PostConstruct
由於雲上 ES 集羣只開放了一個主節點的訪問端口,嗅探獲得的 IP 其實是不能用的。這也解釋了爲什麼第一次訪問是成功的(第一次訪問的主節點開放),後續訪問大概率是失敗的(其它節點端口不開放)。
- 自動嗅探邏輯
主要的改造就是禁用自動更新節點邏輯,位於ElasticSearchClint
文件。在改造的過程中,發現已經有參數elasticsearch.ignore-publish-address
可以滿足需求,但是在去年 8 月的時候 DB、SQL 的文檔裏竟然沒有記錄這個參數,github 上搜索一波發現已有 issue 了,目前社區已經補齊了文檔。
- 忽略嗅探 IP
總結:Airlift
後臺框架雖然沒有文檔,但開發者還是要認真看。
- Oteam 共建 ===========
在去年,隨着 Presto 在騰訊內部的應用場景越來越多,爲了整合各部門的研發能力和技術成果,公司內部由 PCG 歐拉數據中臺牽頭髮起了 Presto Oteam 項目,主要 for Presto 引擎的研發。作爲資訊業務的數據工程同學,我們也有幸參與共建。Oteam 部分工作內容如下:
-
Hive 語義兼容,函數遷移
-
RBO/CBO 執行解析器
-
Worker Tag 能力
-
分析函數開發
-
語法 / 語義擴展
-
動態數據源支持
-
查詢性能優化專項
-
Coordinator 執行流程優化
-
bug fix ...
限於篇幅,簡單介紹第一點:標量函數開發原理。
5.1 函數開發
不同於 Hive UDF 函數可以由用戶直接上傳,在 Presto 引擎中所有擴展部件都以插件形式被統一整合。除了最常見的連接器(Connector)插件以外,函數也是一種插件。如果業務需要自定義函數,就需要單獨開發函數插件。Presto 引擎自帶了很多函數,可以作爲開發者的參考。總共有兩種函數開發方式:
-
使用註解框架的普通函數
-
使用字節碼適配的變長參數函數
第一種方式需要使用 Presto 引擎的註解框架,官網給的例子比較簡單,各種註解搭配使用的方式實際比較複雜。同時函數的數據類型需要涉及到 Presto 引擎的Slice
,Block
等類型,有一定學習成本。第二種方式比較少見,而且不支持通過插件進行開發,只能寫到presto-main
模塊中,它基於 Presto 自帶的字節碼框架動態生成字節碼(包com.facebook.presto.sql.gen
),是比較 hack 的實現,可以參考ArrayConcatFunction
。
5.2 函數註解框架
以標量函數爲例。函數開發和普通的 Java 方法編寫本質上是一樣的,但是也有很多差異點:
-
需要使用註解(annotation)標記出該函數是一個可供調用的標量函數,包括函數名,返回類型、參數類型等。
-
java 原生類型和 Presto 類型有一一對應的關係。Java 的
Slice
對應 Presto 中的Varchar
類型,Java 的Block
對應 Presto 中的Array
類型。(下文分別稱爲 Java 類型和 SQL 類型) -
這些特定的 Java 類型邏輯上等價於
String
,Array數組
,但是 API 差別很大,前期有一定的上手成本。 -
函數有兩套簽名。基於反射可以獲取 Java 類型的形參、返回值類型,稱爲方法簽名。基於
@SqlType
註解可以獲取 Presto 引擎使用的參數、返回值類型,稱爲函數簽名。這裏做個嚴格的區分。 -
可以使用
@TypeParameter
函數註解引入泛型變量。在函數體聲明相關的泛型參數,供SqlType
引用。 -
可以使用
@LiteralParameter
函數註解引入字面量變量。 -
可以使用形參註解
@TypeParameter
、@LiteralParameter
、@FunctionDependency
、@OperatorDependency
聲明一些依賴型參數,在調用函數之前,Presto 會根據解析出來的元數據,自動注入參數依賴。
我們把寫在函數體 / 類名上的註解稱爲函數註解,寫在函數形參前面的註解稱爲形參註解,方便下文引用。一般來說,關注前四點就夠了。後面是一些進階的使用技巧。
按註解類型區分:
以下是官網 [2] 的一個例子:
public class ExampleNullFunction
{
@ScalarFunction("is_null", calledOnNullInput = true)
@Description("Returns TRUE if the argument is NULL")
@SqlType(StandardTypes.BOOLEAN)
public static boolean isNull(@SqlNullable @SqlType(StandardTypes.VARCHAR) Slice string)
{
return (string == null);
}
}
對應剛剛說到的幾點:
-
isNull 函數體有三個註解,
@ScalarFunction
定義了函數名和calledOnNullInput
屬性。@Description
定義了函數的描述字段,在 Presto 客戶端用 show functions 命令可以看到函數的描述信息。@SqlType
描述了函數的返回值類型。這些是函數註解。 -
形參的 SQL 類型是 VARCHAR,Java 類型是 Slice。如果 Slice 換成其它類型,函數調用會失敗。這個是形參註解。
-
返回值、形參都有
@SqlType
註解,它們定義了 SQL 類型。在 Presto 引擎層面,基本都是使用 SQL 類型來進行解析。
再來看另外一個例子:
@ScalarFunction(name = "is_null", calledOnNullInput = true)
@Description("Returns TRUE if the argument is NULL")
public final class IsNullFunction
{
@TypeParameter("T")
@SqlType(StandardTypes.BOOLEAN)
public static boolean isNullSlice(@SqlNullable @SqlType("T") Slice value)
{
return (value == null);
}
@TypeParameter("T")
@SqlType(StandardTypes.BOOLEAN)
public static boolean isNullLong(@SqlNullable @SqlType("T") Long value)
{
return (value == null);
}
@TypeParameter("T")
@SqlType(StandardTypes.BOOLEAN)
public static boolean isNullDouble(@SqlNullable @SqlType("T") Double value)
{
return (value == null);
}
// ...and so on for each native container type
}
可以看到,在函數體,多了@TypeParameter
函數註解,引入了一個泛型變量T
,可以在形參註解中被 @SqlType 引用。@SqlType
註解的類型聲明爲T
以後,這幾個函數的函數簽名都是一樣的。在Presto
引擎看來,這幾個函數擁有相同的函數簽名,是一類函數。
其中,有很多細節的問題其實需要看源碼才知道需要怎麼寫。比如,細心的同學從上面兩個例子可以發現:
-
問:爲什麼第二個例子的
@ScalarFunction
和@Description
註解是寫在類名上面而不是函數名上面? -
答:寫在類名上,代表這個類中的所有方法的函數簽名都是一樣的,由一個
ParameticScalar
類進行管理。 -
問:Slice 和 Java 的 String 是什麼關係,需要怎麼處理轉換?
-
答:參考其它函數的實現,可以調用
toStringUtf8()
轉換成String
類型再做處理。 -
問:哪些註解是一定要寫的?哪些是可選的?哪些是在某些條件下需要同時出現的?
-
答:比如說
@ScalarFunction
中的calledOnNullInput
屬性,當形參中有以下任意註解 (@SqlNullable
,@BlockPosition
,@IsNull
) 的時候,需要指定爲 true,默認爲 false。
雖然 Presto 文檔只講了冰山一角,但是引擎內部自帶了很多函數,是非常有價值的參考資料。這裏有很多細節,需要看 Presto 源碼才能得到答案。以上只是註解的使用,具體這個自定義函數後續如何被 Presto 引擎解析,不關注問題也不大,註解寫錯了大部分 case 也會在插件裝載的時候被識別出來。推薦高階開發者看看ParametricScalarImplementation
中標量函數的解析流程。
5.3 常用註解參考手冊
以下總結了註解框架中的一些常用註解。建議有一定基礎後作爲參考來看。
@ScalarFunction: 函數註解。定義函數的名稱,別名,可見性,純函數性,是否處理空值。
@Description: 函數註解。描述函數功能的字符串。在 presto 客戶端使用show functions
命令可以查看。
@TypeParameter: 函數註解、形參註解。對於函數註解,聲明一個泛型變量,形參註解中的 @SqlType 可以使用它,在解析函數調用的時候會嘗試將泛型類型和具體類型進行綁定。對於形參註解,它引入一個依賴型參數。
@LiteralParameters: 函數註解、形參註解。對於函數註解,定義一些字面量變量,長整數類型。如果有@Constraint
函數註解,則需要滿足它定義的表達式條件。對於形參註解,它引入一個依賴型參數。
@SqlType: 函數註解、形參註解。定義形參、返回值的 SQL 類型。大概可以分以下幾種:
@SqlNullale:函數註解、形參註解。對於函數註解,表示返回值類型是否可能爲空。原始類型(e.g. int)不需要註解,包裝類型和其它類型需要聲明該註解。對於形參註解,如果該位置的實參是 null,依然執行函數體。默認情況遇到 null 直接返回 null。參考InterpretedFunctionInvoker
的空值處理邏輯。
依賴型參數:一種形參註解。函數中需要用到的一些變量,從 SQL 語句自動推導而來。這些形參由框架處理,用戶不感知。
15.contains 函數
可以看到,contains
函數有多種類型,但是函數簽名都是一樣的。由於在函數中需要根據實際類型來調用接口讀取元素,T 的實際類型必須通過形參的方式傳遞進來,但是用戶寫 SQL 的時候並不用顯式指定類型,因爲它可以自動推導出來,這裏涉及到 methodHandle 的綁定參數,就不詳細展開了。總之,雖然contains()
有四個參數,但是用戶只感知最後兩個。
5.4 變長參數函數
一些變長參數的函數,比如 tHive 中的parse_simple_json
函數,在 ETL 任務中一次調用解多個 key,是比較高效的。雖然是變長參數,但是這裏的變長,是相對不同用戶提交的 SQL 語句而言。而用戶每一次提交的 SQL,其實參數個數都是確定的,沒有必要用變長參數,e.g. 對於一個 SQL,代碼中的parse_simple_json(d4, 'key1', 'key2')
,其實參數就是三個。函數聲明爲變長,但是實際中根據每個 SQL 語句轉成定長參數。針對這種情況,Presto 引擎並沒有使用註解框架。而是採用了比較 hack 的方式,直接定義一個內部函數類,裏面有一個形參爲數組的業務函數。通過引擎自帶的字節碼生成模塊,把它適配成一個定長參數函數。大概原理如下所示:
- 字節碼動態適配
最後附上標量函數註冊的流程圖,希望能對函數註冊的流程有更直觀的理解。
-
函數註冊流程
-
技術輸出 =======
從去年下半年開始入門 Presto 引擎開發,接觸下來感覺從零起步確實不易。雖然仔細搜索還是能找到一些不錯的資料,但是 Presto 相關的官方文檔相對於其他大數據組件來說是偏少的。比如基礎的 Airlift 框架,官方文檔僅有一句話介紹。爲了降低後續同事的學習成本,這裏特地把一些知識點梳理成腦圖(逐步完善中),也供大家參考。大多數子節點都能用一章的篇幅來展開描述,可見快速培養出一個優秀的 Presto 開發者還是不太容易。以後有機會我們也會輸出一系列技術文集。
- 基礎知識
-
執行相關概念
-
騰訊內部應用概覽 ===========
最後列出部分騰訊內部應用的 Presto 情況。
7.1 應用場景
-
TEG - 大數據平臺統一 SQL 引擎 SuperSQL,Presto 作爲計算引擎融合的一部分,實現聯邦數據訪問,計算加速等功能,支持交互式數據分析場景
-
PCG - 歐拉中臺,在數據質量監控和資產洞察以及在線數據服務的數據裝載中,作爲計算查詢引擎
-
TEG/CSIG 聯合 - 雲原生數據湖計算 DLC,用戶使用標準 SQL 即可完成對象存儲服務(COS)及其他雲端數據設施的聯合建模、分析,無服務器架構(Serverless Presto)作爲底層計算引擎
-
CSIG - 雲日誌服務 CLS,擴展了大量自定義 SQL 函數,以豐富 PB 級日誌實時 SQL 分析能力。Presto 支持底層存儲解耦,提供不同場景日誌需求,以及異構存儲聯合查詢
-
CSIG - 醫療資訊與服務部,作爲業務線數據服務平臺聯邦查詢引擎,統一查詢前端語言,計劃打通用戶狀態存儲的 MySQL、流水日誌存儲的 ES、用戶行爲數倉的 Hive/Clickhouse/Iceberg 等
-
PCG - 騰訊看點,連接部門內 20 餘個異構數據源的聯邦查詢引擎,適配了騰訊內部的 Hive/ES/CH/Redis 等數據源。
-
IEG - 數據中臺,作爲數據查詢服務聯邦查詢,Adhoc 場景執行引擎
7.2 合作生態
騰訊內部通過Oteam
的方式來組織跨 BG / 部門的開源協同共建。目前和 Presto 關係比較密切的 Oteam 有 Alluxio、Iceberg、Impala。Alluxio 緩存技術已經在 TEG 的部分場景落地使用了。TEG 大數據團隊也和 Presto/Trino 社區同時提出了各自的 Iceberg Connector PR。Impala 在騰訊燈塔平臺已經有非常成熟的應用落地,未來和 Presto 一起加強對 MPP 引擎發展的探討。期待未來 Oteam 組織以及其它大數據團隊能有更深入的合作,助力 Presto 在更多業務中落地推廣。
- 後續計劃 =======
除了繼續在 Presto 引擎層面進行深耕優化,聯邦查詢引擎的應用層功能需要繼續豐富,還有很多用例需要去探索。基於數據分析同學的反饋,很多複雜的預處理邏輯以往需要 spark scala 或者 pyspark 進行處理,現在基本都可以用 Presto 代替了,後續如果能把模型訓練等調包流程整合到一起,也許能夠提供上手成本更低的數據分析體驗,也是一個值得探索的方向。最後,我們希望在服務好業務的前提下,進行一系列高質量的技術輸出來提升部門的技術影響力。
-
結合業務場景,完成引擎和相關連接器的優化改造
-
豐富聯邦查詢引擎應用層功能
-
數據科學引擎
-
強化技術輸出
- 新書推薦 =======
封面第一印象:Presto 運行 SQL,就像青蛙喫蟲子一樣快?
本書的內容質量是毋庸置疑的。對於初學者來說,左手官網文檔,右手《Presto 實戰》進行入門應該是標準姿勢。其行文的層次性、結構性,內容的完整性、權威性,對高手來說是一本非常好的字典。推薦給有興趣的同學~
Matt Fuller、Manfred Moser、Martin Traverso 著
張晨 黃鵬程 傅宇 譯
SQL 領域重磅力作,Presto 官方指南
Presto 創始團隊、Kafka 聯合創作者推薦
多位國內一線技術大咖力薦
亞馬遜全五星好評
文章封面介紹:CH-54 Tarhe “塔赫”運輸直升機。雖然沒有運輸艙,僅是一個飛行載具,但是它可以靈活運輸集裝箱、火炮、輕型載具、直升機等物資,如果真要丟個炸彈,也是可以的。這和 Presto 存算分離的思想如出一轍,結合當前部門特性選擇了掛載醫療艙的 “塔赫” 作爲封面。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/7T00d_0inKXmXH-Su0Ncqg