---
title: '玩转 Databend UDF'
date: 2025-06-12
slug: category-product/databend-udf
cover_url: 'databend-udf-cn-cover.jpeg'
description: '本文将深入探讨三种 UDF 形态：Lambda UDF、UDF Script 和 External UDF Server，并通过具体的案例展示它们的实现方式，最后进行性能对比分析…'
tags: [databend, UDF]
category: Product
authors:
  - sundyli
---

# 玩转 Databend UDF

## 引言

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

{/* truncate */}

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

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

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

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

Lambda UDF 示例：

```SQL
🐳 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。

```SQL
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
$$;

╭─────────────────╮
│  fib_python(10) │
│ Nullable(Int32) │
├─────────────────┤
│              55 │
╰─────────────────╯
```

- Javascript UDF

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

```SQL
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 依赖

```TOML
[package]
name = "arrow-udf-example"
version = "0.1.0"

[lib]
crate-type = ["cdylib"]

[dependencies]
arrow-udf = "0.8"
```

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

```Rust
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
}
```

3. 然后编译到 wasm32-wasip1

```Rust
cargo build --release --target wasm32-wasip1
```

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

```Rust
🐳 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 示例）：

```Python
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()
```

2. 在 Databend 中注册外部函数：

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

3. 调用示例：

```SQL
🐳 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) 在不同 UDF 实现下 的性能对比：

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

SQL:

```Rust
select fib((n % 10) ::Int32) from range(1, 1000000) t(n) ignore_result;
```

WASM UDF 表现最为出色，适合高性能处理

| UDF 类型       | 平均每行数据执行耗时 (us) | 适用场景           |
| -------------- | ------------------------- | ------------------ |
| Lambda UDF     | -                         | 简单转换、快速原型 |
| Python UDF     | 0.18                      | 复杂逻辑、AI 集成  |
| JavaScript UDF | 2.68                      | 轻量级数据处理     |
| WASM UDF       | 0.11                      | 高性能处理         |
| External UDF   | 23.2                      | 大规模数据处理     |

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

性能优化建议：

1. 简单逻辑优先使用 Lambda UDF
2. 复杂计算考虑 Python/JavaScript UDF
3. 内部业务场景使用 External UDF 并部署多个服务节点

## 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/WASM 实现完整算法逻辑              | 通过 Arrow Flight RPC 服务暴露计算接口  |
| 适用场景 | 快速原型/简单转换                     | AI 集成/复杂数据处理                         | 高并发/分布/内部业务                    |

## 结语：扩展您的数据能力

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

参考文档：

- 查看 UDF 开发文档：[UDF 指南](https://docs.databend.cn/guides/query/udf#embedded-udfs)
- 参与社区讨论：[Github](https://github.com/datafuselabs/databend)
