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_partitionProcedure的代码是这样的:

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个参数,前两个参数名字分别叫schematable,类型都是 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内置提供的机制,做起事情来比较“合规”,有需要的场景不妨试试。

上一篇:火眼金睛看Java基本类型(上) | 带你学《Java编程入门》之三


下一篇:教程:使用Data Lake Analytics读/写MongoDB数据