Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En esta página se describe cómo crear Scala y Java funciones definidas por el usuario (UDF), registrarlas en el catálogo de Unity y compartirlas en entornos de proceso. Las UDF del catálogo de Unity le permiten reutilizar la lógica de JVM existente con controles de acceso y gobernanza del catálogo de Unity.
A diferencia de las UDF de Scala con ámbito de sesión limitadas a un único cuaderno o clúster, las UDF registradas en el catálogo de Unity son:
- Gobernado: Administrado con permisos y controles de acceso de Unity Catalog.
- Reutilizable: compartido entre equipos, cuadernos, trabajos y almacenes de SQL.
- Detectable: visible en el Explorador de catálogos y las tablas del sistema.
- Aislado: Se ejecuta en entornos aislados con un coste único de arranque en frío por sesión. Las llamadas posteriores son rápidas.
Requisitos
Su área de trabajo debe estar habilitada para Unity Catalog. Se aplican los siguientes requisitos adicionales.
Proceso: se admiten todos los tipos de proceso, incluidos cuadernos y trabajos sin servidor, almacenes de SQL y canalizaciones declarativas de Spark en Lakeflow. El proceso clásico requiere Databricks Runtime 18.2 o superior. En la computación sin servidor y los almacenes SQL, la definición de UDF debe especificar la versión 4 del entorno o una superior en el campo environment_version. Este requisito se aplica a la definición de UDF, no al cuaderno o al trabajo que realiza la llamada. Consulte las versiones del entorno sin servidor .
Desarrollo:
- Scala: 2.13.16. No se admite Scala 2.12.
- JDK: 17.
- Empaquetado: un archivo JAR fat que contiene todas las dependencias de terceros usadas por la UDF.
Permisos:
- Cree una UDF:
USAGEyCREATE FUNCTIONen el esquema yUSAGEen el catálogo. - Ejecute una UDF:
EXECUTEen la función yUSAGEen el esquema y el catálogo. - Acceda al archivo JAR:
READ VOLUMEen el volumen donde se almacena el archivo JAR.
Consulte Administrar privilegios en el catálogo de Unity para obtener más información sobre los permisos del catálogo de Unity.
Compila tu archivo JAR de UDF
Empaquete el código compilado como un ARCHIVO JAR y cárguelo en un volumen de catálogo de Unity antes de registrar la UDF. Elija un método de compilación:
Compilar localmente
Siga estos pasos para crear un archivo JAR fat mediante un entorno de desarrollo local.
Configuración del entorno
Instale las herramientas necesarias en el equipo local. Los siguientes comandos son para macOS. Para otras plataformas, instale JDK 17 y sbt (Scala) o Maven (Java) mediante el administrador de paquetes de la plataforma.
Scala
Instale JDK 17 y sbt:
brew install openjdk@17
brew install sbt
Compruebe la instalación:
java -version # Should show Java 17
sbt --version # Should show sbt version
Java
Instale JDK 17 y Maven:
brew install openjdk@17
brew install maven
Compruebe la instalación:
java -version # Should show Java 17
mvn --version # Should show Maven version
Creación del proyecto
Configure un proyecto en Scala o Java.
Scala
Cree un nuevo proyecto de Scala mediante sbt:
sbt new scala/scala-seed.g8
Cuando se le solicite, escriba un nombre de proyecto (por ejemplo, my-udf-project).
Configuración de build.sbt
Reemplace el contenido del build.sbt archivo por la siguiente configuración:
scalaVersion := "2.13.16"
ThisBuild / organization := "com.example"
lazy val myUDF = (project in file("."))
.settings(
name := "my-udf"
)
Habilitación del complemento sbt-assembly
Cree o edite project/assembly.sbt y agregue:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.0.0")
Este complemento crea un archivo JAR fat que contiene todas las dependencias.
Java
Cree un nuevo proyecto de Maven mediante el arquetipo de inicio rápido:
mvn archetype:generate \
-DgroupId=com.example \
-DartifactId=my-udf \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=false
Este comando crea la estructura de proyecto estándar de Maven con src/main/java directorios y src/test/java .
Configuración de pom.xml
Dentro de las etiquetas <project></project>, en el archivo pom.xml generado, añada un bloque <properties> con la siguiente configuración:
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
También dentro de las <project></project> etiquetas, agregue un <build> bloque con la siguiente configuración:
<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>
maven-shade-plugin crea un archivo JAR fat que contiene todas las dependencias.
Escribe tu UDF
Al escribir la UDF, consulte Tipos de datos para conocer los tipos de datos admitidos y las asignaciones de lenguaje para ver cómo se asignan los tipos de Scala y Java a tipos SQL.
El controlador de UDF debe cumplir los siguientes requisitos:
-
Scala: defina el manejador como un método de un
object(no de unclass). El valorHANDLERcorresponde a un método en unobjectde Scala. -
Java: defina el controlador como método
public static. -
Firma: los tipos y el orden de los parámetros del método, así como el tipo de retorno, deben coincidir con la lista de argumentos y con el tipo
RETURNSde la instrucciónCREATE FUNCTION. - Solo valores escalares: El controlador debe devolver un único valor escalar. No se admiten los tipos de retorno de tabla.
- Autocontenido: El manejador debe operar solo con sus argumentos de entrada. No puede usar las API de Spark ni depender de los paquetes principales de Spark. Consulte Limitaciones.
Note
Para Scala, se omite un controlador con un tipo de parámetro primitivo (como Int) y devuelve NULL cuando cualquier argumento de entrada es SQL NULL. Para recibir y manejar los valores de NULL, envuelve el parámetro en Option, como Option[Int].
Scala
Cree un objeto Scala en src/main/scala/com/example/MyUDF.scala y defina la función UDF.
Ejemplo básico
package com.example
object MyUDF {
def addOne(x: Int): Int = x + 1
}
Ejemplo con dependencia externa
Para usar bibliotecas externas, agréguelas al build.sbt archivo:
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"
)
)
A continuación, usa la dependencia en la 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")
}
}
}
Pruebe la UDF con pruebas unitarias antes de la implementación. Consulte Pruebas de UDF localmente.
Java
Cree una clase Java en src/main/java/com/example/MyUDF.java y defina la UDF como un método estático público.
Ejemplo básico
package com.example;
public class MyUDF {
public static int addOne(int x) {
return x + 1;
}
}
Ejemplo con dependencia externa
Para usar bibliotecas externas, agréguelas a la <dependencies> sección del pom.xml archivo:
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
</dependencies>
Luego, utilice la dependencia en su 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);
}
}
Pruebe la UDF con pruebas unitarias antes de la implementación. Consulte Pruebas de UDF localmente.
Note
La UDF se ejecuta en un espacio aislado sin sesión activa de Spark, por lo que no puede usar las API de Spark desde el cuerpo de la función. Por ejemplo, no puede crear ni trabajar con DataFrames o conjuntos de datos, ejecutar spark.sql(...) ni acceder a SparkSession o SparkContext. La UDF debe ser una lógica autosuficiente que opere únicamente con sus argumentos de entrada. Tampoco puede depender de los paquetes principales de Spark.
Crea tu fat JAR
Compile el proyecto para crear un archivo JAR fat que contenga todas las dependencias.
Scala
En el directorio raíz del proyecto, ejecute:
sbt clean assembly
El JAR fat se crea en target/scala-2.13/ con un nombre como my-udf-assembly-0.1.0-SNAPSHOT.jar.
Java
En el directorio raíz del proyecto, ejecute:
mvn clean package
El JAR fat se crea en target/ con un nombre como my-udf-1.0-SNAPSHOT.jar.
Carga tu archivo JAR en un volumen de Unity Catalog
Si aún no tiene un volumen de Catálogo de Unity, cree uno:
CREATE VOLUME IF NOT EXISTS my_catalog.my_schema.udf_jars
COMMENT 'Storage for UDF JAR files';
Si otros usuarios necesitan ejecutar la función UDF, concédales READ VOLUME sobre el volumen:
GRANT READ VOLUME ON VOLUME my_catalog.my_schema.udf_jars TO `user@example.com`;
Suba su archivo JAR al volumen mediante Catalog Explorer:
- En el área de trabajo de Azure Databricks, haga clic en
Catalog para abrir el Explorador de catálogos.
- Seleccione el catálogo y, a continuación, seleccione el esquema que contiene el volumen.
- Haga clic en el nombre del volumen.
- Haga clic en Subir a este volumen y seleccione su archivo JAR.
- Haga clic en Cargar.
- Una vez completada la carga, haga clic en el nombre del archivo JAR.
- Haga clic en Copiar ruta para copiar la ruta del volumen en el portapapeles. Por ejemplo,
/Volumes/my_catalog/my_schema/udf_jars/my-udf-assembly-0.1.0-SNAPSHOT.jar(Scala) o/Volumes/my_catalog/my_schema/udf_jars/my-udf-1.0-SNAPSHOT.jar(Java). Necesitará esta ruta de acceso cuando registre la UDF.
Compilación en un cuaderno
Puede compilar una UDF, empaquetarla como un archivo JAR y cargarla en un volumen de Unity Catalog directamente desde un cuaderno de Azure Databricks. Este enfoque funciona para UDF pequeñas y sin dependencias. Para las UDFs con bibliotecas de terceros, use Compilar localmente.
La siguiente celda de Python escribe una UDF de Java que limpia una cadena (recorta los espacios en blanco, contrae los espacios repetidos y convierte el texto a minúsculas), la compila con JDK 17, la empaqueta como un archivo JAR y la copia a un volumen de Unity Catalog. Actualice el/la volume_path para que apunte a un volumen existente sobre el que tenga WRITE VOLUME permisos.
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}")
Después de que el archivo JAR esté en el volumen, registre la UDF. Use LANGUAGE JAVA y establezca HANDLER en el método totalmente cualificado, como com.databricks.udf.StringCleanUDF.clean.
Registra tu UDF en Unity Catalog
Después de generar y subir el archivo JAR, utilice la instrucción CREATE FUNCTION para registrar su UDF en 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';
La CREATE FUNCTION instrucción usa los parámetros siguientes:
LANGUAGE: el idioma de la UDF.HANDLER: ruta de acceso completa al método, con el formato'package.Object.method'(Scala) o'package.ClassName.method'(Java).DETERMINISTIC: declara que la función siempre devuelve la misma salida para la misma entrada, lo que permite la optimización de consultas.Note
Quite
DETERMINISTICsi la función llama a API externas o tiene cualquier otro comportamiento no determinista.ENVIRONMENT: define el entorno de ejecución para la UDF.-
java_dependencies: matriz JSON de rutas de acceso a archivos JAR en los volúmenes del catálogo de Unity. Esta es la ruta de acceso del archivo que copió en el paso anterior. Use comillas simples para la matriz y comillas dobles para las rutas. -
environment_version: debe ser'4'o superior para Scala y Java UDF. La versión 4 del entorno especifica Scala 2.13.16 y JDK 17. Consulte las versiones del entorno sin servidor .
-
Llamada a la UDF en SQL y cuadernos
Después del registro, puede llamar a la UDF en consultas SQL, cuadernos y vistas:
-- 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;
Gobernanza y uso compartido
Use permisos de Catálogo de Unity para controlar quién puede ejecutar la UDF y para que sea reconocible en toda la organización.
Concesión de permisos
Use el Explorador de catálogos o SQL para conceder los permisos necesarios para que otros usuarios ejecuten las UDF.
Explorador de catálogos
- En la barra lateral, haga clic en
Catálogo.
- Seleccione el catálogo y, a continuación, seleccione el esquema que contiene la función.
- Haga clic en el nombre de la función.
- En la pestaña Permisos , haga clic en Conceder.
- Seleccione las entidades de seguridad a las que desea conceder acceso y seleccione el permiso
EXECUTE. - Haga clic en Confirmar.
SQL
Ejecute el siguiente comando en un cuaderno o en el editor de SQL de Databricks para conceder EXECUTE permisos a un usuario o 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`;
Revocar permisos
Use el Explorador de catálogos o SQL para revocar permisos de otros usuarios.
Explorador de catálogos
- En la barra lateral, haga clic en
Catálogo.
- Seleccione el catálogo y, a continuación, seleccione el esquema que contiene la función.
- Haga clic en el nombre de la función.
- En la pestaña Permisos , active la casilla situada junto a la entidad de seguridad desde la que desea revocar el acceso. Haga clic en Revocar.
- En la notificación, haga clic en Revocar.
SQL
Ejecute el siguiente comando en un cuaderno o en el editor de SQL de Databricks para revocar EXECUTE permisos de un usuario o 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`;
Detección de UDF
Para encontrar las UDF gestionadas en Unity Catalog, consulte la tabla information_schema.routines, sustituyendo los valores my_catalog y 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';
Actualización de la UDF
Para actualizar una UDF de catálogo de Unity existente con código nuevo:
- Realice cambios en el código localmente.
- Vuelva a generar el archivo JAR con un nuevo número de versión.
- Scala:
sbt clean assembly(por ejemplo,my-udf-assembly-0.2.0-SNAPSHOT.jar) - Java:
mvn clean package(por ejemplo,my-udf-2.0-SNAPSHOT.jar)
- Scala:
- Suba el nuevo archivo JAR al volumen de Unity Catalog.
- Use
CREATE OR REPLACE FUNCTIONcon el mismo nombre de función para actualizar la UDF. Compruebe que hace referencia al archivo JAR más reciente enjava_dependencies.
Azure Databricks usa el nuevo código en la siguiente invocación. No es necesario reiniciar el clúster.
Optimización del rendimiento
Latencia de inicio en frío
La primera llamada ADF de una sesión inicializa el espacio aislado, que agrega latencia. Las llamadas posteriores en la misma sesión son más rápidas. Tenga en cuenta esto al realizar pruebas comparativas o diseñar cargas de trabajo sensibles a la latencia.
Almacenamiento en caché de cálculos costosos
Si la UDF realiza una inicialización o cálculo costosos, almacene en caché el resultado para calcularlo solo una vez.
Scala
Use un val campo en el objeto Scala para almacenar en caché el 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 un static campo con un bloque de inicializador estático para almacenar en caché el 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);
}
}
Utilice DETERMINISTIC cuando corresponda
Marque la UDF como DETERMINISTIC si siempre generara la misma salida para la misma entrada. Esto permite al optimizador de consultas almacenar en caché los resultados y mejorar el rendimiento.
Limitaciones
- Solo se admiten UDF escalares. No se admiten funciones de agregado definidas por el usuario (UDF) ni funciones de tabla definidas por el usuario (UDF).
- Las UDF se ejecutan en un espacio aislado sin sesión activa de Spark. Las API de Spark (
SparkSession,SparkContext,spark.sql(...), operaciones de DataFrame y Dataset) no están disponibles. - Las UDF no pueden depender de los paquetes principales de Spark.
- Las UDF no tienen acceso a los archivos del área de trabajo ni a los volúmenes del catálogo de Unity en tiempo de ejecución.
procedimientos recomendados
Databricks recomienda los procedimientos siguientes:
- Asigne una versión a los archivos JAR. Por ejemplo:
my-udf-0.1.0.jar,my-udf-0.2.0.jar. - Valide las asignaciones de tipos de SQL antes de la implementación. Consulte Asignaciones de idioma.
- Conceda permisos de
READ VOLUMEy deEXECUTEsolo a los usuarios que necesiten ejecutar la UDF. Utilice la propiedad del grupo para las UDF compartidas por varios equipos.
Probar UDF localmente
Pruebe la UDF con pruebas unitarias antes de realizar la implementación en producción.
Scala
Para probar src/main/scala/example/MyUDF.scala, cree un archivo de prueba en 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)
}
}
Agregue la dependencia de prueba a build.sbt:
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.15" % Test
Para ejecutar las pruebas:
sbt test
Java
Para probar src/main/java/com/example/MyUDF.java, cree un archivo de prueba en 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));
}
}
Añada la dependencia de JUnit a la sección <dependencies> de pom.xml:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
Para ejecutar las pruebas:
mvn test