首页 > 数据库 > 国内数据库 > 玩转 Databend UDF
Databend 作为新一代云原生数据仓库,提供了六百多个内置函数,满足了大部分用户的需求。然而,随着业务的增长,需求也变的日新月异,内置的函数可能无法服务用户变化的需求。在这种场景下, Databend 提供了多种用户自定义函数(UDF)实现方式,满足不同场景下的数据处理需求。
本文将深入探讨三种 UDF 形态:Lambda UDF、UDF Script 和 External UDF Server,并通过具体的案例展示它们的实现方式,最后进行性能对比分析。
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 │
╰─────────────────────────╯
它的特点:
Databend 引擎中支持内嵌的多语言执行器, 可以执行通过 Python、JavaScript 和 WASM 编写 UDF Script,适合复杂业务逻辑实现。
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 │ ╰─────────────────╯
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 中,示例教程。
[package] name = "arrow-udf-example" version = "0.1.0" [lib] crate-type = ["cdylib"] [dependencies] arrow-udf = "0.8"
#[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 │ ╰─────────────────────╯
通过 Arrow Flight 协议与外部 UDF Server 通信,适合将已有的服务和 Databend 互联互通。
Example:
from databend_udf import udf, UDFServer import 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()
🐳 root@default:) CREATE OR REPLACE FUNCTION fib_server (INT) RETURNS INT LANGUAGE python HANDLER = 'fib' ADDRESS = 'http://0.0.0.0:8815';
🐳 root@default:) select fib_server(10); ╭─────────────────╮ │ fib_server(10) │ │ Nullable(Int32) │ ├─────────────────┤ │ 55 │ ╰─────────────────╯
特点:
在单机内存环境下(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;
| UDF类型 | 平均每行数据执行耗时(us) | 适用场景 |
|---|---|---|
| Lambda UDF | - | 简单转换、快速原型 |
| Python UDF | 0.18 | 复杂逻辑、AI 集成 |
| JavaScript UDF | 2.68 | 轻量级数据处理 |
| WASM UDF | 0.11 | 高性能处理 |
| External UDF | 23.2 | 大规模数据处理 |
*注:External UDF 耗时包含网络通信,实际处理时间会更短。
性能优化建议:
根据您的业务需求选择合适的 UDF 类型:
| 对比维度 | Lambda UDF | UDF Script | External UDF Server |
|---|---|---|---|
| 开发效率 | ⭐⭐⭐⭐ (纯SQL实现,无需编译) | ⭐⭐⭐ (需编写脚本) | ⭐ (需独立服务部署) |
| 执行性能 | ⭐⭐⭐⭐ (原生性能) | ⭐⭐⭐ (Python/JS运行时开销, wasm性能最佳) | ⭐⭐ (支持批量处理,动态扩容,网络开销) |
| 复杂逻辑 | ⭐ (仅限简单表达式) | ⭐⭐⭐ (支持完整编程语言) | ⭐⭐ (需服务化拆分逻辑) |
| 系统集成 | ⭐ (仅限数据库内部) | ⭐⭐ (需适配语言运行时) | ⭐⭐⭐⭐ (Arrow Flight协议集成) |
| 实现 | (n)->CASE WHEN n<=1 THEN n ELSE ... | Python/JS实现完整算法逻辑 | 通过Arrow Flight RPC服务暴露计算接口 |
| 适用场景 | 快速原型/简单转换 | AI集成/复杂数据处理 | 高并发/分布 |
Databend 的多形态 UDF 支持为数据处理提供了极大的灵活性。无论您需要快速实现简单转换,还是集成复杂业务逻辑,或是构建分布式计算管道,都能找到合适的解决方案。
参考文档:
Databend 是一款开源、弹性、低成本,基于对象存储也可以做实时分析的新式湖仓。期待您的关注,一起探索云原生数仓解决方案,打造新一代开源 Data Cloud。
👨💻 Databend Cloud: databend.cn
📖 Databend 文档: docs.databend.cn
💻 Wechat:Databend
✨ GitHub: github.com/databendlab...
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70042558/viewspace-3087500/ ,如需转载,请注明出处,否则将追究法律责任。