Remote User Defined Function Service

Remote UDF Service 支持通过 RPC 的方式访问用户提供的 UDF Service,以实现用户自定义函数的执行。相比于 Native 的 UDF 实现,Remote UDF Service 有如下优势和限制:

  1. 优势
  • 跨语言:可以用 Protobuf 支持的各类语言编写 UDF Service。
  • 安全:UDF 执行失败或崩溃,仅会影响 UDF Service 自身,而不会导致 Doris 进程崩溃。
  • 灵活:UDF Service 中可以调用任意其他服务或程序库类,以满足更多样的业务需求。
  1. 使用限制
  • 性能:相比于 Native UDF,UDF Service 会带来额外的网络开销,因此性能会远低于 Native UDF。同时,UDF Service 自身的实现也会影响函数的执行效率,用户需要自行处理高并发、线程安全等问题。
  • 单行模式和批处理模式:Doris 原先的的基于行存的查询执行框架会对每一行数据执行一次 UDF RPC 调用,因此执行效率非常差,而在新的向量化执行框架下,会对每一批数据(默认2048行)执行一次 UDF RPC 调用,因此性能有明显提升。实际测试中,基于向量化和批处理方式的 Remote UDF 性能和基于行存的 Native UDF 性能相当,可供参考。

编写 UDF 函数

本小节主要介绍如何开发一个 Remote RPC service。在 samples/doris-demo/udf-demo/ 下提供了 Java 版本的示例,可供参考。

拷贝 proto 文件

拷贝 gensrc/proto/function_service.proto 和 gensrc/proto/types.proto 到 Rpc 服务中

  • function_service.proto
    • PFunctionCallRequest
      • function_name:函数名称,对应创建函数时指定的symbol
      • args:方法传递的参数
      • context:查询上下文信息
    • PFunctionCallResponse
      • result:结果
      • status:状态,0代表正常
    • PCheckFunctionRequest
      • function:函数相关信息
      • match_type:匹配类型
    • PCheckFunctionResponse
      • status:状态,0代表正常

生成接口

通过 protoc 生成代码,具体参数通过 protoc -h 查看

实现接口

共需要实现以下三个方法

  • fnCall:用于编写计算逻辑
  • checkFn:用于创建 UDF 时校验,校验函数名/参数/返回值等是否合法
  • handShake:用于接口探活

创建 UDF

目前暂不支持 UDAF 和 UDTF

  1. CREATE FUNCTION
  2. name ([,...])
  3. [RETURNS] rettype
  4. PROPERTIES (["key"="value"][,...])

说明:

  1. PROPERTIES中symbol表示的是 rpc 调用传递的方法名,这个参数是必须设定的。
  2. PROPERTIES中object_file表示的 rpc 服务地址,目前支持单个地址和 brpc 兼容格式的集群地址,集群连接方式 参考 格式说明Remote User Defined Function Service - 图1 (opens new window)
  3. PROPERTIES中type表示的 UDF 调用类型,默认为 Native,使用 Rpc UDF时传 RPC。
  4. name: 一个function是要归属于某个DB的,name的形式为dbName.funcName。当dbName没有明确指定的时候,就是使用当前session所在的db作为dbName

示例:

  1. CREATE FUNCTION rpc_add(INT, INT) RETURNS INT PROPERTIES (
  2. "SYMBOL"="add_int",
  3. "OBJECT_FILE"="127.0.0.1:9090",
  4. "TYPE"="RPC"
  5. );

使用 UDF

用户使用 UDF 必须拥有对应数据库的 SELECT 权限。

UDF 的使用与普通的函数方式一致,唯一的区别在于,内置函数的作用域是全局的,而 UDF 的作用域是 DB内部。当链接 session 位于数据内部时,直接使用 UDF 名字会在当前DB内部查找对应的 UDF。否则用户需要显示的指定 UDF 的数据库名字,例如 dbName.funcName

删除 UDF

当你不再需要 UDF 函数时,你可以通过下述命令来删除一个 UDF 函数, 可以参考 DROP FUNCTION