天天看點

Presto Procedure

Presto Procedure

概述

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資料得到錯誤的結果, 如下圖:

Presto Procedure

這時候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内置提供的機制,做起事情來比較“合規”,有需要的場景不妨試試。