乐趣区

关于java:Java-UDF-的设计与使用介绍兼容-Hive-UDF-实现数据快速迁移

作者介绍: 李仕杨,SelectDB 生态研发工程师,Apache Doris Contributor。

咱们在应用各个 SQL 引擎时,会遇到纷繁复杂的查问需要。一部分能够通过引擎自带的内置函数去解决,但内置函数往往具备肯定通用性,在局部非凡场景下内置函数可能无奈满足需要,所以个别 SQL 引擎会提供 UDF 性能,不便用户通过本人写逻辑来满足特定的需要,Apache Doris 也不例外。

在 Java UDF 之前,Apache Doris 提供了原生 UDF。因为是应用 C++ 来编写的,执行效率高、速度更快,然而在理论应用中也会存在一些问题:

  • 跟 Doris 代码耦合度高,须要本人打包编译 Doris 源码
  • 只反对 C++ 语言并且 UDF 代码出错会影响 Doris 集群稳定性
  • 对于只相熟 Hive、Spark 等大数据组件的用户有肯定应用门槛

由上可知,原生的 UDF 实现起来门槛较高且存在肯定的不稳定性因素。那么是否有一种实现绝对简略、应用门槛较低且与 Doris 代码耦合度低的 UDF 呢?

答案是有的。在 2022 年 12 月正式公布的 Apache Doris 1.2.0 版本(https://github.com/apache/dor…)中,咱们推出了全新的 Java UDF 和 Remote UDF 性能,其中 Java UDF 不仅能满足以上要求,并且在不便和平安角度为用户带来了全新体验:

  • 不相熟 C++?Java 代码一样能够实现本人的 UDF
  • 应用条件刻薄?只有有 Jar 包就能应用
  • 放心稳定性?Java UDF 出错只影响本身,对 Doris 的稳定性简直无影响
  • 迁徙旧大数据平台的数据和 UDF 费时费力?Java UDF 齐全兼容 Hive UDF,轻松实现疾速迁徙
  • …….

设计思路

大体步骤

Apache Doris 的 BE 是由 C++ 代码编写,如果想在 Doris 中实现 Java UDF,不可避免须要调用 JNI,而不正确的 JNI 调用将导致重大的性能问题。那么该如何设计 Java UDF 以解决这个问题呢?

Doris Java UDF 针对向量化引擎,其设计思路大体如下:

  • 首先,制订用户在创立 UDF 时必须遵循的一些规定。例如,UDF 类必须具备 Evaluate 办法,并且必须是 Public 和 Non-Static 的。这些规定确保咱们能够正确调用 UDF。
  • 其次,Doris 查问引擎会执行一个新的 Java 函数调用,BE 会创立或重用一个 JVM 来调用真正的 Java UDF。为了隔离不同的 UDF 实例,抉择应用不同的类加载器来加载 UDF。
  • 最初,因为执行时是向量化的,因而能够实现一次执行多行数据只调用一次 JNI,起因是 JNI 开销被输出列中所有行摊派了[1],这将给用户带来更好的性能体验。

具体步骤

相熟 Java 的敌人应该都晓得,JVM 在间接内存即非堆区的 IO 操作比堆区更高效,因而 Doris Java UDF 个别是在间接内存中对数据进行 IO 操作。通常 UDF 有以下几种状况:

  • 个别 UDF(定长 UDF)

    • 此处的根本思维是传递间接指向输出缓冲区和输入缓冲区的地址,Doris 能够间接从所给地址中读取和写回数据,这能够帮忙 Doris 防止不必要的数据拷贝。Input Buffer 和 Output Buffer 都是 JVM 的堆外内存,能够间接通过 J ava 的 API 来操作这部分内存。
    • 整体执行模式如下图:
  • UDAF(变长输入)

    • 对于个别 UDF 来说,输入大小和类型是不变,因而所需 Buffer 大小也是确定的,而对于 UDAF(变长输入),个别 UDF(定长 UDF)的步骤将不再实用。
    • 因而 须要做出如下扭转:** 在第 1 步调配一个初始缓冲区,当后果大于调配的初始缓冲区时跳到第 3 步,在第 3 步中进行一次扩容。当这种状况继续产生时,咱们再次反复上述步骤调配新缓冲区并持续为残余的行执行 UDF,直到所有数据都执行实现。
    • 执行过程如下图所示:

通过上文介绍,咱们根本理解了 Doris Java UDF 的执行状况,那么在理论生产中应该如何来应用 Java UDF 呢?

Java UDF 的应用

Java UDF 应用起来非常简单。Java UDF 在 Doris 内注册实现后,Doris 执行时通过调用 jar 包来实现 UDF 逻辑。程序构造如下图:

具体 步骤:

  1. 参考 `doris/samples/doris-demo/java-udf-demo/src/main/java/org/apache/doris/udf/AddOne.java ` 文件,编写 UDF 逻辑,你能够像 Hive UDF 一样在任何中央进行编写和打包,不用跟 Doris 环境相关联。
  • AddOne.java文件内容如下:
  • // Licensed to the Apache Software Foundation (ASF) under one
    // or more contributor license agreements.  See the NOTICE file
    // distributed with this work for additional information
    // regarding copyright ownership.  The ASF licenses this file
    // to you under the Apache License, Version 2.0 (the
    // "License"); you may not use this file except in compliance
    // with the License.  You may obtain a copy of the License at
    //
    //   http://www.apache.org/licenses/LICENSE-2.0
    //
    // Unless required by applicable law or agreed to in writing,
    // software distributed under the License is distributed on an
    // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    // KIND, either express or implied.  See the License for the
    // specific language governing permissions and limitations
    // under the License.
    ​
    package org.apache.doris.udf;
    ​
    import org.apache.hadoop.hive.ql.exec.UDF;
    ​
    public class AddOne extends UDF {public Integer evaluate(Integer value) {return value == null? null: value + 1;}
    }
  • 执行 mvn 打包命令

       mvn clean package
  • 创立 UDF

      CREATE FUNCTION java_udf_name(int) RETURNS int PROPERTIES (
          "file"="file:///path/to/your_jar_name.jar",
          "symbol"="org.apache.doris.udf.AddOne",
          "always_nullable"="true",
          "type"="JAVA_UDF"
      );
  • 应用已创立的 UDF

           建表:CREATE TABLE IF NOT EXISTS test.t1 (`col_1` int NOT NULL)
           DISTRIBUTED BY HASH(col_1) PROPERTIES("replication_num" = "1");
           
           
           插入数据:insert into test.t1 values(1),(2);
           
           
           应用 udf:MySQL [(none)]> select col_1, java_udf_name(col_1) as col_2 from test.t1;
           +------------+------------+
           | col_1      | col_2      |
           +------------+------------+
           | 1          | 2          |
           | 2          | 3          |
           +------------+------------+

    至此,Doris Java UDF 的创立和应用就实现了,非常简略易用。

    注意事项

  • 最开始须要确定 BE 节点是否配置了 JAVA_HOME,如果环境变量没有配置,则能够在 be/bin/start_be.sh 文件第一行加上

    export JAVA_HOME=/xxx/xxx
  • UDF 代码中必须要带有以下信息(UDAF 则替换成对应的)

    import org.apache.hadoop.hive.ql.exec.UDF;
  • 创立 Doris java UDF 的语句,其格局如下

    CREATE FUNCTION name ([,...])
    [RETURNS] rettype
    PROPERTIES (["key"="value"][,...])  
  • 例子中残缺的 SQL 如下

     CREATE FUNCTION java_udf_name(int) RETURNS int PROPERTIES (
    "file"="file:///path/to/your_jar_name.jar",
    "always_nullable"="true",
    "type"="JAVA_UDF"
        );

    java_udf_name 是创立 UDF 的名称,能够进行更改,UDF 名称不能与 Doris 其余函数重命。

    名称后的 `(int) ` 示意函数输出参数是 int 类型,` RETURNS 后的 int ` 示意函数输入也是 int 类型;输入输出类型跟 Java 代码中 Evaluate 函数的输入输出类型要保持一致。

  • PROPERTIES

    file 示意 jar 包在本机的门路,应该批改 "/path/to/your_jar_name.jar"` 作为 jar 包的绝对路径。如果是多机环境,也能够应用 http 模式示意的门路,例如“file”=”http://${host}:${http_port}/${your_jar_file}”`

    能够应用 python 命令来简略启动一个 http server:nohup python -m SimpleHTTPServer 12345 > /dev/null 2>&1
 Symbol 能够参考 Java 代码中的 Package
`always_nullable` 示意 UDF 返回后果中是否可能呈现 NULL 值,如果想要在计算中对呈现的 NULL 值有非凡解决,以确定后果中不会返回 NULL,能够设为 `false`,有利于晋升整个查问计算过程的性能。# 收益总结

通过本文的介绍,理解了 Doris Java UDF 的设计与应用办法,那么在理论的利用中,Doris Java UDF 能为使用者带来什么收益呢?-   相熟 Java 的同学也能够疾速上手开发 Doris,应用简略便捷,较大晋升开发效率。-   兼容 Hive UDF,无效升高从 Hadoop 迁徙数据的老本。-   UDF 代码出错并不会影响 Doris,某种程度上保障了 Doris 的更好稳固运行。-   与 Doris 代码解耦,真正做到了 "Write Once Run Anywhere"

-   执行效率方面,Java UDF 是齐全向量化执行的,一次执行多行数据只调用一次 JNI,联合堆外内存、Zero Copy 等优化技术,用户在应用 Java UDF 时,也能失去与之前的 C++ UDF 统一甚至更佳的查问性能体验。# 社区奉献

如果你的 UDF 已被许多场景利用,能够将 UDF 奉献到 Apache Doris 社区。奉献步骤可参考:<https://doris.incubator.apache.org/zh-CN/docs/dev/ecosystem/udf/contribute-udf>

须要留神的是,Doris BE 端是由 C++ 代码实现的,因而你所奉献的内置 UDF 也须要由 C ++ 代码实现。Apache Doris 社区期待你的退出!** 本文援用 **

[1] Viktor Rosenfeld, René Müller, Pinar Tözün, etc. Processing Java UDFs in a C++ environment. SoCC 2017: 419-431.

[2] Marcel Kornacker, Alexander Behm, Victor Bittorf, etc. Impala: A Modern, Open-Source SQL Engine for Hadoop. CIDR 2015.

[3]DSIP-001: Java UDF:<https://cwiki.apache.org/confluence/display/DORIS/DSIP-001%3A+Java+UDF>

[4]Apache Doris GitHub: <https://github.com/apache/doris>
退出移动版