
概述
Presto裡面有個類似普通資料庫存儲過程的東西叫做
Procedure
(
https://prestosql.io/docs/current/sql/call.html),它的主要作用是用來提供一下DDL或者管理性的工作,它的用法跟MySQL裡面的存儲過程類似,比如:
-- 調用一個兩個參數的存儲過程
CALL test(123, 'apple');
-- 支援named arguments
CALL test(
name => 'apple',
id => 123
);
雖然名字叫Procedure,但是你無法通過Presto的CALL文法來調用底層RDBMS裡面的存儲過程,概念類似,但是不是一個層面的東西,Presto裡面能CALL的Procedure是需要Connector注冊的。
開發一個Procedure
我們來看一下一個Procedure包含哪些部分:
/** 屬于哪個schema */
private final String schema;
/** 名字叫什麼 */
private final String name;
/** 有哪些參數?每個參數的名稱、類型是什麼? */
private final List<Argument> arguments;
/** 由哪個方法實作? */
private final MethodHandle methodHandle;
...
從上面
Procedure
的結構可以看出,真正要做的實作一個方法(
MethodHandle
)就好了,比如我們要drop一個hive partition,它大概的邏輯是這樣的:
public void dropPartition(
ConnectorSession session,
String schema, String table,
List<Object> partitionColNames,
List<Object> partitionValues)
{
metastore.dropPartition(
schema, table,
partitionValues, false
);
}
正式聲明這個
drop_partition
Procedure的代碼是這樣的:
new Procedure(
"system",
"drop_partition",
ImmutableList.of(
new Argument("schema", VARCHAR),
new Argument("table", VARCHAR),
new Argument(
"partitionColumnNames",
"array(varchar)"
),
new Argument(
"partitionValues",
"array(varchar)"
)
),
methodHandle(
DemoProcedure.class,
"dropPartition",
ConnectorSession.class,
String.class,
String.class,
List.class,
List.class
).bindTo(this)
);
從上面兩段代碼我們可以很清楚的看出:
- 它的名字叫
。drop_partition
- 它有4個參數,前兩個參數名字分别叫
和schema
,類型都是table
VARCHAR
- 它的具體的邏輯是由
裡面的DemoProcedure
來實作的(也就是上面第一段代碼)。dropPartition
細心的同學可能注意到了,這個
dropPartition
還有一個
ConnectorSession
類型的參數呀,怎麼沒有聲明出來?沒錯,不過這個參數不用顯式聲明給Procedure的使用者,ConnectorSession裡面包含調用這個Procedure時候的一些上下文資訊,Presto在調用對應MethodHandle的時候發現這種類型的參數會自動傳過來。
開發完成之後通過
com.facebook.presto.spi.connector.Connector#getProcedures
暴露出去就可以調用了。
Procedure的實際使用場景
前面也說過Procedure的使用場景是一些DDL以及管理性的工作,一個典型的例子就是處理Hive Connector裡面 drop table/drop partition的工作,我們知道Hive是有自己的Hive Meta Store的,要實作drop partition,一個不經意的想法是直接利用HiveCli去Hive Meta Store上面去drop,但是這樣會有問題, 原因在于 Presto 裡面對于Hive Meta Store裡面的中繼資料是有緩存的,直接操作Hive Meta Store會使得Presto裡面的中繼資料緩存沒有及時失效掉,導緻使用者通過Presto查詢Hive資料得到錯誤的結果, 如下圖:
這時候Procedure就可以派上用場了,利用我們前面介紹的
drop_partition
, 我們可以通過如下的指令去“徹底地”删除一個partition了:
CALL system.hive.drop_partition(
'db001', 'table001',
ARRAY['dt'], ARRAY['bar']
);
讓使用者直接用這種語句去删除partition當然很傻,你可以稍微包裝一下,使用者寫SQL:
alter table db001.table001 drop partition(dt = 'bar')
, 你背後自動調用
drop_partition
即可。
當然,通過Procedure來解決Presto Connector内部的緩存問題也不是理想的方案,但是是目前Presto架構下比較現實的方案。理想的方案應該讓Presto通過SPI層面暴露出接口來做這種事情。
總結
今天介紹了Presto裡面的Procedure, 可以用它來做一些DDL以及管理的事情,因為是Presto内置提供的機制,做起事情來比較“合規”,有需要的場景不妨試試。