写点什么

玩转 Databend UDF

作者:Databend
  • 2025-06-12
    福建
  • 本文字数:3234 字

    阅读完需:约 11 分钟

玩转 Databend UDF

引言

Databend 作为新一代云原生数据仓库,提供了六百多个内置函数,满足了大部分用户的需求。然而,随着业务的增长,需求也变的日新月异,内置的函数可能无法服务用户变化的需求。在这种场景下, Databend 提供了多种用户自定义函数(UDF)实现方式,满足不同场景下的数据处理需求。


本文将深入探讨三种 UDF 形态:Lambda UDF、UDF Script 和 External UDF Server,并通过具体的案例展示它们的实现方式,最后进行性能对比分析。

Lambda UDF:纯 SQL 定义的函数语法糖

Lambda UDF 是 Databend 中最简单的 UDF 形式,完全通过 SQL 语句定义和执行表达式,适合简单的数据转换和计算。


我们可以在 SQL 中定义一个闭包函数,然后进行调用。


Lambda UDF 示例:


🐳 root@default:) CREATE FUNCTION plus_3 AS (a,b,c) -> a + b + c;
🐳 root@default:) select plus_3(1,2,3);
╭─────────────────╮│ plus_3(1, 2, 3) ││ UInt8 │├─────────────────┤│ 6 │╰─────────────────╯
🐳 root@default:) CREATE FUNCTION age AS (d) -> date_diff(year, d, now());
🐳 root@default:) select age('1992-01-01'::Date);
╭─────────────────────────╮│ age('1992-01-01'::DATE) ││ Int64 │├─────────────────────────┤│ 33 │╰─────────────────────────╯
复制代码


它的特点:


  • 纯 SQL 实现,无需外部语言支持

  • 无法支持递归调用

  • 执行性能受表达式定义影响

UDF Script:多语言扩展能力

Databend 引擎中支持内嵌的多语言执行器, 可以执行通过 Python、JavaScript 和 WASM 编写 UDF Script,适合复杂业务逻辑实现。


  • Python UDF


Databend 使用了 pyo3 引擎来执行 Python script, 我们可以通过如下方式定义一个简单的 Python UDF。


CREATE FUNCTION fib_python ( Int32 ) RETURNS Int32 LANGUAGE python HANDLER = 'fib' AS $$def fib(n):    a, b = 0, 1    for _ in range(n):        a, b = b, a + b    return a$$;
select plus_3(1,2,3);╭─────────────────╮│ plus_3(1, 2, 3) ││ UInt8 │├─────────────────┤│ 6 │╰─────────────────╯
复制代码


  • Javascript UDF


Databend 使用了 rquickjs 引擎来执行 Javascript, 我们可以通过如下方式定义一个简单的 Javascript UDF。


CREATE OR REPLACE FUNCTION fib_js ( Int32 ) RETURNS Int32 LANGUAGE JAVASCRIPT HANDLER = 'fib' AS $$export function fib(n) {    let a = 0, b = 1;    for (let i = 0; i < n; i++) {        [a, b] = [b, a + b];    }    return a}$$;
🐳 root@default:) select fib_js(10);
╭─────────────────╮│ fib_js(10) ││ Nullable(Int32) │├─────────────────┤│ 55 │╰─────────────────╯
复制代码


特点:


  • 支持完整语言特性,实现复杂逻辑

  • Python UDF 特别适合数据科学和 AI 集成

  • JavaScript UDF 适合轻量级数据处理,同时兼顾沙箱安全性

  • UDF script 属于 Databend 企业特性

  • WASM UDF 你可以使用 rust 代码来实现 UDF, 然后编译到 Wasm target 中,示例教程。


  1. 创建一个项目,Cargo.toml 包含 arrow-udf 依赖


[package]name = "arrow-udf-example"version = "0.1.0"
[lib]crate-type = ["cdylib"]
[dependencies]arrow-udf = "0.8"
复制代码


  1. 使用 #[function] 宏来定义你的函数


use arrow_udf::function;
#[function("fib(int) -> int")]fn fib(n: i32) -> i32 { let (mut a, mut b) = (0, 1); for _ in 0..n { let c = a + b; a = b; b = c; } a}
复制代码


然后编译到 wasm32-wasip1


cargo build --release --target wasm32-wasip1  
复制代码


将生成的 result.wasm 文件传到 databend stage 中,并定义 wasm udf 函数


🐳 root@default:) create stage s_udf;🐳 root@default:) put fs:///tmp/arrow_udf_example.wasm @s_udf/;
╭─────────────────────────────────────────────────╮│ file │ status │ size ││ String │ String │ UInt64 │├─────────────────────────────┼─────────┼─────────┤│ /tmp/arrow_udf_example.wasm │ SUCCESS │ 1279392 │╰─────────────────────────────────────────────────╯
🐳 root@default:) CREATE OR REPLACE FUNCTION fib_wasm (INT) RETURNS INT LANGUAGE wasm HANDLER = 'fib' AS $$@s_udf/arrow_udf_example.wasm$$;

🐳 root@default:) select fib_wasm(10::Int32); ╭─────────────────────╮│ fib_wasm(10::Int32) ││ Nullable(Int32) │├─────────────────────┤│ 55 │╰─────────────────────╯
复制代码

External UDF Server:灵活解耦的外部 UDF 服务

通过 Arrow Flight 协议与外部 UDF Server 通信,适合将已有的服务和 Databend 互联互通。


Example:


  1. 启动 UDF Server(Python 示例):


from databend_udf import udf, UDFServerimport logging
logging.basicConfig(level=logging.INFO)
@udf( input_types=["INT"], result_type="INT", skip_null=True,)def fib(n: int) -> int: a, b = 0, 1 for _ in range(n): a, b = b, a + b return a
if __name__ == "__main__": udf_server = UDFServer( location="0.0.0.0:8815" ) udf_server.add_function(fib) udf_server.serve()
复制代码


  1. 在 Databend 中注册外部函数:


🐳 root@default:) CREATE OR REPLACE FUNCTION fib_server (INT) RETURNS INT LANGUAGE python HANDLER = 'fib' ADDRESS = 'http://0.0.0.0:8815';
复制代码


  1. 调用示例:


🐳 root@default:) select fib_server(10);
╭─────────────────╮│ fib_server(10) ││ Nullable(Int32) │├─────────────────┤│ 55 │╰─────────────────╯
复制代码


特点:


  • 需要可靠的网络交互

  • 支持灵活的参数配置:支持批量处理(默认 65536 行/批)

  • 可横向扩展 UDF 服务节点来提高性能

  • 适合与现有微服务架构集成, 与 Databend 服务解耦, 可以与任何支持 Arrow Flight 协议的语言交互

性能对比分析

在单机内存环境下(Databend v1.3.0,16GB RAM),计算 fib(x) 的性能对比:


性能测试环境: Intel(R) Core(TM) i9-12900KF 24C archlinux


SQL:


select fib((n % 10) ::Int32) from range(1, 1000000) t(n) ignore_result;
复制代码



*注:External UDF 耗时包含网络通信,实际处理时间会更短。




性能优化建议:


  1. 简单逻辑优先使用 Lambda UDF

  2. 复杂计算考虑 Python/JavaScript UDF

  3. 高并发场景使用 External UDF 并部署多个服务节点

UDF 选型指南

根据您的业务需求选择合适的 UDF 类型:


结语:扩展您的数据能力

Databend 的多形态 UDF 支持为数据处理提供了极大的灵活性。无论您需要快速实现简单转换,还是集成复杂业务逻辑,或是构建分布式计算管道,都能找到合适的解决方案。


参考文档:


关于 Databend

Databend 是一款开源、弹性、低成本,基于对象存储也可以做实时分析的新式湖仓。期待您的关注,一起探索云原生数仓解决方案,打造新一代开源 Data Cloud。


👨‍💻‍ Databend Cloud:databend.cn


📖 Databend 文档:docs.databend.cn


💻 Wechat:Databend


✨ GitHub:github.com/databendlab...

发布于: 刚刚阅读数: 4
用户头像

Databend

关注

还未添加个人签名 2022-08-25 加入

还未添加个人简介

评论

发布
暂无评论
玩转 Databend UDF_Databend_InfoQ写作社区