Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Esta página descreve como criar o Scala e Java UDFs (funções definidas pelo usuário), registrá-las no Catálogo do Unity e compartilhá-las em ambientes de computação. As UDFs do Catálogo do Unity permitem reutilizar a lógica de JVM existente com controles de governança e acesso do Catálogo do Unity.
Ao contrário dos UDFs Scala com escopo de sessão, que se limitam a um único notebook ou cluster, os UDFs registrados no Unity Catalog são:
- Gerenciado: gerenciado com as permissões e os controles de acesso do Unity Catalog.
- Reutilizável: compartilhado por equipes, notebooks, jobs e SQL warehouses.
- Detectável: Visível no Gerenciador de Catálogos e nas tabelas do sistema.
- Isolado: Execute em sandboxes com um custo único de inicialização a frio por sessão. As chamadas subsequentes são rápidas.
Requirements
Seu workspace deve estar habilitado para o Unity Catalog. Os requisitos adicionais a seguir se aplicam.
Computação: todos os tipos de computação têm suporte, incluindo notebooks e trabalhos sem servidor, sql warehouses e Pipelines Declarativos do Spark no Lakeflow. A computação clássica requer o Databricks Runtime 18.2 ou superior. Na computação sem servidor e em SQL warehouses, a definição da UDF deve especificar a Versão do Ambiente 4 ou superior no campo environment_version. Esse requisito se aplica à definição de UDF, não ao notebook de chamada ou ao trabalho. Consulte Versões do ambiente sem servidor.
Desenvolvimento:
- Scala: 2.13.16. Não há suporte para Scala 2.12.
- JDK: 17.
- Empacotamento: um JAR gordo que contém todas as dependências de terceiros usadas pela UDF.
Permissões:
- Crie uma UDF:
USAGEeCREATE FUNCTIONno esquema eUSAGEno catálogo. - Execute uma UDF:
EXECUTEpara a função eUSAGEpara o esquema e o catálogo. - Acesse o arquivo JAR:
READ VOLUMEno volume em que o JAR está armazenado.
Consulte Gerenciar privilégios no Catálogo do Unity para obter mais informações sobre as permissões do Catálogo do Unity.
Criar seu JAR UDF
Empacote o código compilado como um JAR e carregue-o em um volume do Catálogo do Unity antes de registrar o UDF. Escolha um método de build:
Compilar localmente
Siga estas etapas para criar um JAR gordo usando um ambiente de desenvolvimento local.
Configure seu ambiente
Instale as ferramentas necessárias no computador local. Os comandos a seguir são para macOS. Para outras plataformas, instale o JDK 17 e o SBT (Scala) ou o Maven (Java) usando o gerenciador de pacotes da plataforma.
Scala
Instale o JDK 17 e o sbt:
brew install openjdk@17
brew install sbt
Verifique sua instalação:
java -version # Should show Java 17
sbt --version # Should show sbt version
Java
Instale o JDK 17 e o Maven:
brew install openjdk@17
brew install maven
Verifique sua instalação:
java -version # Should show Java 17
mvn --version # Should show Maven version
Criar seu projeto
Configure um projeto no Scala ou Java.
Scala
Crie um novo projeto Scala usando sbt:
sbt new scala/scala-seed.g8
Quando solicitado, insira um nome de projeto (por exemplo, my-udf-project).
Configurar build.sbt
Substitua o conteúdo do build.sbt arquivo pela seguinte configuração:
scalaVersion := "2.13.16"
ThisBuild / organization := "com.example"
lazy val myUDF = (project in file("."))
.settings(
name := "my-udf"
)
Habilitar o plug-in do sbt-assembly
Crie ou edite project/assembly.sbt e adicione:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.0.0")
Esse plug-in cria um JAR gordo que contém todas as suas dependências.
Java
Crie um projeto maven usando o arquétipo de início rápido:
mvn archetype:generate \
-DgroupId=com.example \
-DartifactId=my-udf \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=false
Esse comando cria a estrutura de projeto padrão do Maven com os diretórios src/main/java e src/test/java.
Configurar pom.xml
No arquivo pom.xml gerado, dentro das tags <project></project>, adicione um bloco <properties> com a seguinte configuração:
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
Também dentro das <project></project> marcas, adicione um <build> bloco com a seguinte configuração:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
O maven-shade-plugin cria um JAR completo que contém todas as dependências.
Escreva sua UDF
Ao escrever sua UDF, consulte os tipos de dados para tipos de dados compatíveis e mapeamentos de idioma para ver como os tipos Scala e Java são mapeados para tipos SQL.
Seu manipulador UDF deve atender aos seguintes requisitos:
-
Scala: defina o manipulador como um método em um
object(não em umclass). O valorHANDLERcorresponde a um método emobjectScala. -
Java: defina o manipulador como um
public staticmétodo. -
Assinatura: os tipos dos parâmetros, sua ordem e o tipo de retorno do método devem corresponder à lista de argumentos e ao
RETURNStipo na instruçãoCREATE FUNCTION. - Somente escalar: o manipulador deve retornar um único valor escalar. Não há suporte para tipos de retorno de tabela.
- Autocontido: o manipulador deve operar apenas com base em seus argumentos de entrada. Ele não pode usar APIs do Spark ou depender de pacotes principais do Spark. Confira Limitações.
Note
Para Scala, um manipulador com um tipo de parâmetro primitivo (como Int) é ignorado e retorna NULL quando qualquer argumento de entrada é SQL NULL. Para receber e manipular NULL valores, encapsule o parâmetro em Option, como Option[Int].
Scala
Crie um objeto src/main/scala/com/example/MyUDF.scala Scala e defina sua função UDF.
Exemplo básico
package com.example
object MyUDF {
def addOne(x: Int): Int = x + 1
}
Exemplo com dependência externa
Para usar bibliotecas externas, adicione-as ao arquivo build.sbt :
scalaVersion := "2.13.16"
ThisBuild / organization := "com.example"
lazy val myUDF = (project in file("."))
.settings(
name := "currency-udf",
libraryDependencies ++= Seq(
"org.apache.commons" % "commons-lang3" % "3.12.0"
)
)
Em seguida, use a dependência em sua UDF:
package com.example
import org.apache.commons.lang3.StringUtils
object CurrencyUDF {
private val rates: Map[String, Double] = Map(
"USD" -> 1.0,
"EUR" -> 1.1,
"GBP" -> 1.3,
"JPY" -> 0.007
)
def convertToUSD(price: Double, currency: String): Double = {
require(currency != null, "Currency must not be null")
val normalizedCurrency = StringUtils.upperCase(currency)
rates.get(normalizedCurrency) match {
case Some(rate) => price * rate
case None => throw new IllegalArgumentException(s"Unsupported currency: $currency")
}
}
}
Teste o UDF com testes de unidade antes da implantação. Consulte Como testar UDFs localmente.
Java
Crie uma classe src/main/java/com/example/MyUDF.java Java e defina sua UDF como um método estático público.
Exemplo básico
package com.example;
public class MyUDF {
public static int addOne(int x) {
return x + 1;
}
}
Exemplo com dependência externa
Para usar bibliotecas externas, adicione-as na seção <dependencies> do seu arquivo pom.xml:
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
</dependencies>
Em seguida, use a dependência em sua UDF:
package com.example;
import org.apache.commons.lang3.StringUtils;
import java.util.Map;
import java.util.HashMap;
public class CurrencyUDF {
private static final Map<String, Double> rates = new HashMap<>();
static {
rates.put("USD", 1.0);
rates.put("EUR", 1.1);
rates.put("GBP", 1.3);
rates.put("JPY", 0.007);
}
public static double convertToUSD(double price, String currency) {
if (currency == null) {
throw new IllegalArgumentException("Currency must not be null");
}
String normalizedCurrency = StringUtils.upperCase(currency);
if (!rates.containsKey(normalizedCurrency)) {
throw new IllegalArgumentException("Unsupported currency: " + currency);
}
return price * rates.get(normalizedCurrency);
}
}
Teste o UDF com testes de unidade antes da implantação. Consulte Teste de UDFs localmente.
Note
O UDF é executado em uma área restrita isolada sem sessão ativa do Spark, portanto, ele não pode usar APIs do Spark de dentro do corpo da função. Por exemplo, você não pode criar ou operar em DataFrames ou conjuntos de dados, executar spark.sql(...)ou acessar SparkSession ou SparkContext. A UDF deve consistir em uma lógica autocontida baseada em seus argumentos de entrada. Ele também não pode depender de pacotes principais do Spark.
Criar seu JAR gordo
Crie seu projeto para criar um JAR gordo contendo todas as dependências.
Scala
No diretório raiz do projeto, execute:
sbt clean assembly
O fat JAR é criado em target/scala-2.13/ com um nome semelhante a my-udf-assembly-0.1.0-SNAPSHOT.jar.
Java
No diretório raiz do projeto, execute:
mvn clean package
O fat JAR é criado em target/ com um nome como my-udf-1.0-SNAPSHOT.jar.
Faça upload do seu JAR para um volume do Unity Catalog
Se você ainda não tiver um volume do Catálogo do Unity, crie um:
CREATE VOLUME IF NOT EXISTS my_catalog.my_schema.udf_jars
COMMENT 'Storage for UDF JAR files';
Se outros usuários precisarem executar o UDF, conceda-lhes READ VOLUME no volume:
GRANT READ VOLUME ON VOLUME my_catalog.my_schema.udf_jars TO `user@example.com`;
Carregue seu arquivo JAR no volume usando o Gerenciador de Catálogos:
- No workspace Azure Databricks, clique em
Catalog para abrir o Catalog Explorer.
- Selecione o catálogo e, em seguida, selecione o esquema que contém o volume.
- Clique no nome do volume.
- Clique em Fazer upload para este volume e selecione seu arquivo JAR.
- Clique em Carregar.
- Após a conclusão do upload, clique no nome do arquivo JAR.
- Clique em Copiar caminho para copiar o caminho do volume para sua área de transferência. Por exemplo,
/Volumes/my_catalog/my_schema/udf_jars/my-udf-assembly-0.1.0-SNAPSHOT.jar(Scala) ou/Volumes/my_catalog/my_schema/udf_jars/my-udf-1.0-SNAPSHOT.jar(Java). Você precisa desse caminho ao registrar a UDF.
Compilar em um notebook
Você pode compilar uma UDF, empacotá-la como um JAR e enviá-la para um volume do Unity Catalog diretamente em um notebook do Azure Databricks. Essa abordagem funciona para UDFs pequenas, sem dependências. Para UDFs com bibliotecas de terceiros, use faça a compilação localmente.
A célula Python a seguir grava um Java UDF que limpa uma cadeia de caracteres (corta o espaço em branco, recolhe espaços repetidos e minúsculas), compila-a com o JDK 17, empacota-a como um JAR e copia-a para um volume do Catálogo do Unity. Atualize o volume_path para apontar para um volume existente ao qual você tem WRITE VOLUME acesso.
import os
import subprocess
import shutil
build_dir = "/tmp/udf_build"
package_dir = f"{build_dir}/src/com/databricks/udf"
classes_dir = f"{build_dir}/classes"
os.makedirs(package_dir, exist_ok=True)
os.makedirs(classes_dir, exist_ok=True)
# The UDF handler: a public static method on a plain Java class.
# The doubled backslashes produce a single backslash in the Java source (\\s+).
udf_code = """package com.databricks.udf;
public class StringCleanUDF {
public static String clean(String input) {
if (input == null) return null;
return input.trim().replaceAll("\\\\s+", " ").toLowerCase();
}
}
"""
with open(f"{package_dir}/StringCleanUDF.java", "w") as f:
f.write(udf_code)
# Compile with JDK 17 to match Environment Version 4.
subprocess.run(
["javac", "--release", "17", "-d", classes_dir, f"{package_dir}/StringCleanUDF.java"],
check=True,
)
# Package the compiled class into a JAR.
jar_path = f"{build_dir}/string_clean_udf.jar"
subprocess.run(["jar", "cf", jar_path, "-C", classes_dir, "."], check=True)
# Copy the JAR to a Unity Catalog volume.
volume_path = "/Volumes/my_catalog/my_schema/udf_jars/string_clean_udf.jar"
os.makedirs(os.path.dirname(volume_path), exist_ok=True)
shutil.copy2(jar_path, volume_path)
print(f"JAR uploaded to: {volume_path}")
Depois que o JAR estiver no volume, registre a UDF. Use LANGUAGE JAVA e defina HANDLER como o nome totalmente qualificado do método, como com.databricks.udf.StringCleanUDF.clean.
Registrar sua UDF no Catálogo do Unity
Depois de criar e fazer upload do seu JAR, use a instrução CREATE FUNCTION para registrar sua UDF no Unity Catalog.
Scala
CREATE OR REPLACE FUNCTION my_catalog.my_schema.add_one(x INT)
RETURNS INT
LANGUAGE SCALA
DETERMINISTIC
ENVIRONMENT (
java_dependencies = '["/Volumes/my_catalog/my_schema/udf_jars/my-udf-assembly-0.1.0-SNAPSHOT.jar"]',
environment_version = '4'
)
HANDLER 'com.example.MyUDF.addOne';
Java
CREATE OR REPLACE FUNCTION my_catalog.my_schema.add_one(x INT)
RETURNS INT
LANGUAGE JAVA
DETERMINISTIC
ENVIRONMENT (
java_dependencies = '["/Volumes/my_catalog/my_schema/udf_jars/my-udf-1.0-SNAPSHOT.jar"]',
environment_version = '4'
)
HANDLER 'com.example.MyUDF.addOne';
A CREATE FUNCTION instrução usa os seguintes parâmetros:
LANGUAGE: o idioma da UDF.HANDLER: caminho totalmente qualificado para o método, no formato'package.Object.method'(Scala) ou'package.ClassName.method'(Java).DETERMINISTIC: declara que a função sempre retorna a mesma saída para a mesma entrada, habilitando a otimização de consulta.Note
Remova
DETERMINISTICse sua função chamar APIs externas ou tiver qualquer outro comportamento não determinístico.ENVIRONMENT: define o ambiente de execução para a UDF.-
java_dependencies: uma matriz JSON de caminhos de arquivo JAR em seus volumes do Catálogo do Unity. Esse é o caminho do arquivo copiado na etapa anterior. Use aspas simples ao redor da matriz e aspas duplas ao redor dos caminhos. -
environment_version: deve ser'4'ou posterior para UDFs de Scala e Java. A versão 4 do ambiente especifica Scala 2.13.16 e JDK 17. Consulte Versões do ambiente sem servidor.
-
Chame sua UDF em SQL e notebooks
Após o registro, você pode usar a UDF em consultas SQL, notebooks e views:
-- Simple select
SELECT my_catalog.my_schema.add_one(5) AS result;
-- With table data
SELECT
id,
price,
currency,
my_catalog.my_schema.convert_to_usd(price, currency) AS price_usd
FROM my_catalog.my_schema.transactions;
-- Filtering
SELECT *
FROM my_catalog.my_schema.products
WHERE my_catalog.my_schema.convert_to_usd(price, currency) > 100;
-- Aggregation
SELECT
category,
SUM(my_catalog.my_schema.convert_to_usd(price, currency)) AS total_usd
FROM my_catalog.my_schema.sales
GROUP BY category;
Governança e compartilhamento
Use as permissões do Catálogo do Unity para controlar quem pode executar o UDF e torná-lo detectável em toda a sua organização.
Conceder permissões
Use o Gerenciador de Catálogos ou o SQL para conceder as permissões necessárias para que outros usuários executem suas UDFs.
Gerenciador de Catálogos
- Na barra lateral, clique no
Catálogo.
- Selecione o catálogo e, em seguida, selecione o esquema que contém sua função.
- Clique no nome da função.
- Na guia Permissões, clique em Conceder.
- Selecione as entidades às quais você deseja conceder acesso e selecione a permissão
EXECUTE. - Clique em Confirmar.
SQL
Execute o comando a seguir em um notebook ou no editor de SQL do Databricks para conceder EXECUTE permissões a um usuário ou grupo.
-- Grant to a specific user
GRANT EXECUTE ON FUNCTION my_catalog.my_schema.add_one TO `user@example.com`;
-- Grant to a group
GRANT EXECUTE ON FUNCTION my_catalog.my_schema.add_one TO `data-engineers`;
Revogar permissões
Use o Gerenciador de Catálogos ou o SQL para revogar permissões de outros usuários.
Gerenciador de Catálogos
- Na barra lateral, clique no
Catálogo.
- Selecione o catálogo e, em seguida, selecione o esquema que contém sua função.
- Clique no nome da função.
- Na guia Permissões, marque a caixa de seleção ao lado da entidade de segurança da qual você deseja revogar o acesso. Clique em Revogar.
- Na notificação, clique em Revogar.
SQL
Execute o comando a seguir em um notebook ou no editor de SQL do Databricks para revogar EXECUTE permissões de um usuário ou grupo.
-- Revoke from specific user
REVOKE EXECUTE ON FUNCTION my_catalog.my_schema.add_one FROM `user@example.com`;
-- Revoke from a group
REVOKE EXECUTE ON FUNCTION my_catalog.my_schema.add_one FROM `data-engineers`;
Conheça as UDFs
Para encontrar UDFs gerenciadas no Unity Catalog, consulte a tabela information_schema.routines, substituindo os valores de my_catalog e my_schema:
SELECT
routine_catalog,
routine_schema,
routine_name,
routine_definition,
created
FROM system.information_schema.routines
WHERE routine_catalog = 'my_catalog'
AND routine_schema = 'my_schema';
Atualize sua UDF
Para atualizar um UDF do Catálogo do Unity existente com o novo código:
- Faça alterações no código localmente.
- Recompile o JAR com um novo número de versão.
- Scala:
sbt clean assembly(por exemplo,my-udf-assembly-0.2.0-SNAPSHOT.jar) - Java:
mvn clean package(por exemplo,my-udf-2.0-SNAPSHOT.jar)
- Scala:
- Faça upload do novo JAR para o volume do Unity Catalog.
- Use
CREATE OR REPLACE FUNCTIONcom o mesmo nome de função para atualizar a UDF. Verifique se você referencia o JAR mais recente em suajava_dependencies.
Azure Databricks usa o novo código na próxima invocação. Você não precisa reiniciar o cluster.
Otimização do desempenho
Latência de inicialização a frio
A primeira chamada UDF em uma sessão inicializa a área restrita isolada, o que adiciona latência. As chamadas subsequentes na mesma sessão são mais rápidas. Contabiliza isso ao fazer benchmark ou criar cargas de trabalho sensíveis à latência.
Armazenamento em cache de computações custosas
Se a UDF executar uma inicialização ou um cálculo custoso, armazene o resultado em cache para calculá-lo apenas uma vez.
Scala
Use um val campo no objeto Scala para armazenar em cache o resultado:
package example
object CachedUDF {
// Computed once and cached
val expensiveData: Map[String, Double] = {
// Load data from somewhere expensive
Map("key1" -> 1.0, "key2" -> 2.0)
}
def lookup(key: String): Double = {
expensiveData.getOrElse(key, 0.0)
}
}
Java
Use um static campo com um bloco de inicializador estático para armazenar em cache o resultado:
package example;
import java.util.Map;
import java.util.HashMap;
public class CachedUDF {
// Computed once and cached
private static Map<String, Double> expensiveData;
static {
// Load data from somewhere expensive
expensiveData = new HashMap<>();
expensiveData.put("key1", 1.0);
expensiveData.put("key2", 2.0);
}
public static double lookup(String key) {
return expensiveData.getOrDefault(key, 0.0);
}
}
Use DETERMINISTIC quando apropriado
Marque sua UDF como DETERMINISTIC se ela sempre produzsse a mesma saída para a mesma entrada. Isso permite que o otimizador de consulta armazene resultados em cache e melhore o desempenho.
Limitações
- Há suporte apenas para UDFs escalares. Não há suporte para FUNÇÕES de agregação definidas pelo usuário (UDAFs) e UDTFs (funções de tabela definidas pelo usuário).
- As UDFs são executadas em uma área restrita isolada sem sessão ativa do Spark. As APIs do Spark (
SparkSession,SparkContext,spark.sql(...)dataframe e operações de conjunto de dados) não estão disponíveis. - UDFs não podem depender de pacotes principais do Spark.
- As UDFs não têm acesso a arquivos do workspace nem a volumes do Unity Catalog em tempo de execução.
Práticas recomendadas
O Databricks recomenda as seguintes práticas:
- Versão dos arquivos JAR. Por exemplo,
my-udf-0.1.0.jar,my-udf-0.2.0.jar. - Valide os mapeamentos de tipo SQL antes da implantação. Consulte mapeamentos de idioma.
- Conceda as permissões
READ VOLUMEeEXECUTEsomente aos usuários que precisam executar a UDF. Use a propriedade de grupo em UDFs compartilhadas entre equipes.
Testando UDFs localmente
Teste seu UDF usando testes de unidade antes de implantar em produção.
Scala
Para testar src/main/scala/example/MyUDF.scala, crie um arquivo de teste em src/test/scala/example/MyUDFTest.scala:
package example
import org.scalatest.funsuite.AnyFunSuite
class MyUDFTest extends AnyFunSuite {
test("addOne should add 1 to input") {
assert(MyUDF.addOne(5) == 6)
}
test("addOne should handle negative numbers") {
assert(MyUDF.addOne(-1) == 0)
}
}
Adicione a dependência de teste a build.sbt:
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.15" % Test
Para executar os testes:
sbt test
Java
Para testar src/main/java/com/example/MyUDF.java, crie um arquivo de teste em src/test/java/com/example/MyUDFTest.java:
package com.example;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class MyUDFTest {
@Test
public void testAddOne() {
assertEquals(6, MyUDF.addOne(5));
}
@Test
public void testAddOneWithNegativeNumbers() {
assertEquals(0, MyUDF.addOne(-1));
}
}
Adicione a dependência do JUnit à seção <dependencies> do seu pom.xml:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
Para executar os testes:
mvn test