概述
Presto里面有个类似普通数据库存储过程的东西叫做 Procedure
, 它的主要作用是用来提供一下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内置提供的机制,做起事情来比较“合规”,有需要的场景不妨试试。