Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Halaman ini menjelaskan cara membuat Scala dan Java fungsi yang ditentukan pengguna (UDF), mendaftarkannya di Unity Catalog, dan membagikannya di seluruh lingkungan komputasi. Unity Catalog UDF memungkinkan Anda menggunakan kembali logika JVM yang ada dengan tata kelola Dan kontrol akses Katalog Unity.
Tidak seperti UDF Scala yang dicakup dalam satu sesi yang terbatas pada satu notebook atau kluster, UDF yang terdaftar di Unity Catalog adalah:
- Diatur: Dikelola dengan izin Unity Catalog dan kontrol akses.
- Dapat digunakan kembali: Dibagikan di seluruh tim, notebook, pekerjaan, dan gudang SQL.
- Dapat ditemukan: Terlihat di Catalog Explorer dan tabel sistem.
- Terisolasi: Jalankan di kotak pasir dengan biaya cold-start satu kali per sesi. Pemanggilan berikutnya akan cepat.
Persyaratan
Ruang kerja Anda harus diaktifkan agar dapat menggunakan Katalog Unity. Persyaratan tambahan berikut berlaku.
Komputasi: Semua jenis komputasi didukung, termasuk notebook dan pekerjaan tanpa server, gudang SQL, dan Spark Declarative Pipelines di Lakeflow. Komputasi klasik memerlukan Databricks Runtime 18.2 atau lebih tinggi. Pada komputasi tanpa server dan gudang SQL, definisi UDF harus menentukan Lingkungan Versi 4 atau lebih tinggi di environment_version bidang . Persyaratan ini berlaku untuk definisi UDF, bukan untuk buku catatan panggilan atau pekerjaan. Lihat versi lingkungan Tanpa Server.
Pengembangan:
- Scala: 2.13.16. Scala 2.12 tidak didukung.
- JDK: 17.
- Kemasan: JAR lemak yang berisi semua dependensi pihak ketiga yang digunakan oleh UDF.
Izin:
- Buat UDF:
USAGEdanCREATE FUNCTIONpada skema, danUSAGEpada katalog. - Jalankan UDF:
EXECUTEpada fungsi, danUSAGEpada skema dan katalog. - Akses file JAR:
READ VOLUMEpada volume tempat JAR disimpan.
Lihat Mengelola hak istimewa di Katalog Unity untuk informasi selengkapnya tentang izin Katalog Unity.
Membangun JAR UDF Anda
Kemas kode yang dikompilasi sebagai JAR dan unggah ke volume Katalog Unity sebelum mendaftarkan UDF. Pilih metode build:
Bangun secara lokal
Ikuti langkah-langkah berikut untuk membuat fat JAR menggunakan lingkungan pengembangan lokal.
Atur lingkungan Anda
Instal alat yang diperlukan di komputer lokal Anda. Perintah berikut adalah untuk macOS. Untuk platform lain, instal JDK 17 dan sbt (Scala) atau Maven (Java) menggunakan manajer paket platform Anda.
Scala
Instal JDK 17 dan sbt:
brew install openjdk@17
brew install sbt
Verifikasi penginstalan Anda:
java -version # Should show Java 17
sbt --version # Should show sbt version
Java
Instal JDK 17 dan Maven:
brew install openjdk@17
brew install maven
Verifikasi penginstalan Anda:
java -version # Should show Java 17
mvn --version # Should show Maven version
Membuat proyek Anda
Siapkan proyek di Scala atau Java.
Scala
Buat proyek Scala baru menggunakan sbt:
sbt new scala/scala-seed.g8
Saat diminta, masukkan nama proyek (misalnya, my-udf-project).
Konfigurasikan build.sbt
Ganti konten file Anda build.sbt dengan konfigurasi berikut:
scalaVersion := "2.13.16"
ThisBuild / organization := "com.example"
lazy val myUDF = (project in file("."))
.settings(
name := "my-udf"
)
Aktifkan plugin sbt-assembly
Buat atau edit project/assembly.sbt dan tambahkan:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.0.0")
Plugin ini membuat berkas JAR lengkap yang berisi semua dependensi Anda.
Java
Buat proyek Maven baru menggunakan arketipe mulai cepat:
mvn archetype:generate \
-DgroupId=com.example \
-DartifactId=my-udf \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=false
Perintah ini membuat struktur proyek Maven standar dengan src/main/java direktori dan src/test/java .
Konfigurasikan pom.xml
Dalam file pom.xml yang dihasilkan, di dalam tag <project></project>, tambahkan blok <properties> dengan konfigurasi berikut:
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
Juga di dalam tag <project></project>, tambahkan blok <build> dengan konfigurasi berikut:
<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 membuat fat JAR yang berisi seluruh dependensi Anda.
Tulis UDF Anda
Saat menulis UDF Anda, lihat Tipe data untuk tipe data yang didukung dan Pemetaan bahasa untuk melihat bagaimana tipe Scala dan Java dipetakan ke tipe SQL.
Handler UDF Anda harus memenuhi persyaratan berikut:
-
Scala: Tentukan handler sebagai metode pada
object(bukanclass). NilaiHANDLERmerujuk ke sebuah metode padaobjectScala. -
Java: Tentukan handler sebagai
public staticmetode. -
Tanda tangan: Jenis parameter metode, urutan, dan jenis pengembalian harus sesuai dengan daftar argumen dan
RETURNSketik pernyataan AndaCREATE FUNCTION. - Skalar saja: Handler harus mengembalikan nilai skalar tunggal. Jenis pengembalian tabel tidak didukung.
- Mandiri: Handler harus beroperasi hanya pada argumen inputnya. Ini tidak dapat menggunakan API Spark atau bergantung pada paket inti Spark. Lihat Batasan.
Note
Untuk Scala, handler dengan jenis parameter primitif (seperti Int) dilewati dan dikembalikan NULL ketika argumen input apa pun adalah SQL NULL. Untuk menerima dan menangani nilai NULL, bungkus parameter dengan Option, misalnya Option[Int].
Scala
Buat objek Scala di src/main/scala/com/example/MyUDF.scala dan tentukan fungsi UDF Anda.
Contoh dasar
package com.example
object MyUDF {
def addOne(x: Int): Int = x + 1
}
Contoh dengan dependensi eksternal
Untuk menggunakan pustaka eksternal, tambahkan pustaka tersebut ke file build.sbt Anda:
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"
)
)
Kemudian gunakan dependensi di UDF Anda:
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")
}
}
}
Uji UDF Anda dengan uji unit sebelum menerapkannya. Lihat Menguji UDF secara lokal.
Java
Buat kelas Java di src/main/java/com/example/MyUDF.java dan tentukan UDF Anda sebagai metode statis publik.
Contoh dasar
package com.example;
public class MyUDF {
public static int addOne(int x) {
return x + 1;
}
}
Contoh dengan dependensi eksternal
Untuk menggunakan pustaka eksternal, tambahkan pustaka tersebut ke bagian <dependencies> dari file pom.xml Anda:
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
</dependencies>
Kemudian gunakan dependensi di UDF Anda:
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);
}
}
Uji UDF Anda dengan uji unit sebelum menerapkannya. Lihat Menguji UDF secara lokal.
Note
UDF Anda berjalan di kotak pasir terisolasi tanpa sesi Spark aktif, sehingga tidak dapat menggunakan API Spark dari dalam isi fungsi. Misalnya, Anda tidak dapat membuat atau mengoperasikan dataFrame atau Himpunan Data, menjalankan spark.sql(...), atau mengakses SparkSession atau SparkContext. UDF harus berupa logika mandiri atas argumen inputnya. Ini juga tidak dapat bergantung pada paket inti Spark.
Buat fat JAR Anda
Bangun proyek Anda untuk membuat fat JAR yang berisi semua dependensi.
Scala
Dari direktori akar proyek Anda, jalankan:
sbt clean assembly
fat JAR dibuat di target/scala-2.13/ dengan nama seperti my-udf-assembly-0.1.0-SNAPSHOT.jar.
Java
Dari direktori akar proyek Anda, jalankan:
mvn clean package
Fat JAR dibuat di target/ dengan nama seperti my-udf-1.0-SNAPSHOT.jar.
Unggah JAR Anda ke volume Unity Catalog
Jika Anda belum memiliki volume Unity Catalog, buatlah volume tersebut:
CREATE VOLUME IF NOT EXISTS my_catalog.my_schema.udf_jars
COMMENT 'Storage for UDF JAR files';
Jika pengguna lain perlu menjalankan UDF, berikan READ VOLUME pada volume:
GRANT READ VOLUME ON VOLUME my_catalog.my_schema.udf_jars TO `user@example.com`;
Unggah file JAR Anda ke volume menggunakan Catalog Explorer:
- Di ruang kerja Azure Databricks Anda, klik
Katalog untuk membuka Catalog Explorer.
- Pilih katalog, lalu pilih skema yang berisi volume Anda.
- Klik nama volume.
- Klik Unggah ke volume ini dan pilih file JAR Anda.
- Klik Unggah.
- Setelah pengunggahan selesai, klik nama file JAR Anda.
- Klik Salin jalur untuk menyalin jalur volume ke clipboard Anda. Misalnya,
/Volumes/my_catalog/my_schema/udf_jars/my-udf-assembly-0.1.0-SNAPSHOT.jar(Scala) atau/Volumes/my_catalog/my_schema/udf_jars/my-udf-1.0-SNAPSHOT.jar(Java). Anda memerlukan jalur ini saat mendaftarkan UDF.
Menyusun di buku catatan
Anda dapat mengkompilasi UDF, mengemasnya sebagai JAR, dan mengunggahnya ke volume Katalog Unity langsung dari buku catatan Azure Databricks. Pendekatan ini cocok untuk UDF kecil yang tidak memiliki dependensi. Untuk UDF dengan pustaka pihak ketiga, gunakan Bangun secara lokal.
Sel Python berikut menulis Java UDF yang membersihkan string (memangkas spasi kosong, menciutkan spasi berulang, dan huruf kecil), mengkompilasinya dengan JDK 17, mengemasnya sebagai JAR, dan menyalinnya ke volume Katalog Unity. Perbarui volume_path agar mengarah ke volume yang sudah ada dan yang Anda miliki izin WRITE VOLUME untuk mengaksesnya.
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}")
Setelah JAR berada dalam volume, daftarkan UDF. Gunakan LANGUAGE JAVA dan atur HANDLER ke metode yang sepenuhnya memenuhi syarat, seperti com.databricks.udf.StringCleanUDF.clean.
Daftarkan UDF Anda di Unity Catalog
Setelah Anda membuat dan mengunggah JAR, gunakan CREATE FUNCTION pernyataan untuk mendaftarkan UDF Anda di Katalog Unity.
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';
Pernyataan CREATE FUNCTION menggunakan parameter berikut:
LANGUAGE: Bahasa UDF.HANDLER: Jalur yang sepenuhnya memenuhi syarat ke metode , dalam format'package.Object.method'(Scala) atau'package.ClassName.method'(Java).DETERMINISTIC: Menyatakan bahwa fungsi selalu mengembalikan output yang sama untuk input yang sama, memungkinkan pengoptimalan kueri.Note
Hapus
DETERMINISTICjika fungsi Anda memanggil API eksternal atau memiliki perilaku non-deterministik lainnya.ENVIRONMENT: Menentukan lingkungan eksekusi untuk UDF.-
java_dependencies: Larik JSON yang berisi jalur file JAR di volume Unity Catalog Anda. Ini adalah jalur file yang Anda salin di langkah sebelumnya. Gunakan tanda kutip tunggal di sekitar array dan tanda kutip ganda di sekitar jalur. -
environment_version: Harus'4'atau lebih tinggi untuk UDF Scala dan Java. Environment Version 4 menentukan Scala 2.13.16 dan JDK 17. Lihat versi lingkungan Tanpa Server.
-
Panggil UDF Anda di SQL dan buku catatan
Setelah pendaftaran, Anda dapat memanggil UDF dalam kueri, buku catatan, dan tampilan SQL:
-- 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;
Tata kelola dan berbagi
Gunakan izin Unity Catalog untuk mengontrol siapa yang dapat menjalankan UDF Anda dan membuatnya dapat ditemukan di seluruh organisasi Anda.
Memberikan izin
Gunakan Catalog Explorer atau SQL untuk memberikan izin yang diperlukan bagi pengguna lain untuk menjalankan UDF Anda.
Eksplorer Katalog
- Di bar samping, klik
Katalog.
- Pilih katalog, lalu pilih skema yang berisi fungsi Anda.
- Klik nama fungsi.
- Di tab Izin , klik Berikan.
- Pilih prinsipal yang ingin Anda berikan akses, lalu pilih izin
EXECUTE. - Klik tombol Konfirmasi.
SQL
Jalankan perintah berikut ini di notebook atau editor Databricks SQL untuk memberikan EXECUTE izin kepada pengguna atau grup.
-- 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`;
Mencabut izin
Gunakan Catalog Explorer atau SQL untuk mencabut izin dari pengguna lain.
Eksplorer Katalog
- Di bar samping, klik
Katalog.
- Pilih katalog, lalu pilih skema yang berisi fungsi Anda.
- Klik nama fungsi.
- Di tab Izin , pilih kotak centang di samping prinsipal yang ingin Anda cabut aksesnya. Klik Batalkan.
- Di pemberitahuan, klik Cabut.
SQL
Jalankan perintah berikut ini di notebook atau editor Databricks SQL untuk mencabut EXECUTE izin dari pengguna atau grup.
-- 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`;
Jelajahi UDF
Untuk menemukan UDF yang dikelola di Unity Catalog, kuerilah tabel information_schema.routines, dengan mengganti nilai my_catalog dan 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';
Memperbarui UDF Anda
Untuk memperbarui Unity Catalog UDF yang ada dengan kode baru:
- Buat perubahan pada kode Anda secara lokal.
- Bangun ulang JAR dengan nomor versi baru.
- Scala:
sbt clean assembly(misalnya,my-udf-assembly-0.2.0-SNAPSHOT.jar) - Java:
mvn clean package(misalnya,my-udf-2.0-SNAPSHOT.jar)
- Scala:
- Unggah JAR baru ke volume Katalog Unity.
- Gunakan
CREATE OR REPLACE FUNCTIONdengan nama fungsi yang sama untuk memperbarui UDF. Pastikan bahwa Anda merujuk ke JAR terbaru dijava_dependencies.
Azure Databricks menggunakan kode baru pada pemanggilan berikutnya. Anda tidak perlu menghidupkan ulang kluster Anda.
Pengoptimalan performa
Latensi mulai dingin
Panggilan UDF pertama dalam sesi menginisialisasi kotak pasir terisolasi, yang menambahkan latensi. Panggilan berikutnya dalam sesi yang sama lebih cepat. Perhitungkan hal ini saat membuat tolok ukur atau merancang beban kerja yang sensitif terhadap latensi.
Penyimpanan dalam tembolok untuk komputasi yang mahal
Jika UDF Anda melakukan inisialisasi atau komputasi yang mahal, cache hasilnya untuk menghitungnya hanya sekali.
Scala
val Gunakan bidang di objek Scala untuk menyimpan hasilnya:
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
static Gunakan bidang dengan blok penginisialisasi statis untuk menyimpan hasilnya:
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);
}
}
Gunakan DETERMINISTIK jika sesuai
Tandai UDF Anda sebagai DETERMINISTIC jika selalu menghasilkan keluaran yang sama untuk masukan yang sama. Ini memungkinkan pengoptimal kueri untuk menyimpan hasil dan meningkatkan performa.
Keterbatasan
- Hanya UDF skalar yang didukung. Fungsi agregat yang ditentukan pengguna (UDAF) dan fungsi tabel yang ditentukan pengguna (UDTF) tidak didukung.
- UDF berjalan di kotak pasir terisolasi tanpa sesi Spark aktif. API Spark (
SparkSession,SparkContext,spark.sql(...), DataFrame dan operasi Himpunan Data) tidak tersedia. - UDF tidak dapat bergantung pada paket inti Spark.
- UDF tidak memiliki akses ke file workspace atau volume Unity Catalog saat dijalankan.
Praktik terbaik
Databricks merekomendasikan praktik berikut:
- Versi file JAR Anda. Misalnya,
my-udf-0.1.0.jar,my-udf-0.2.0.jar. - Validasi pemetaan jenis SQL sebelum penyebaran. Lihat Pemetaan bahasa.
- Berikan
READ VOLUMEdanEXECUTEizin hanya kepada pengguna yang perlu menjalankan UDF. Gunakan kepemilikan grup untuk UDF yang dibagikan antar tim.
Menguji UDF secara lokal
Uji UDF Anda dengan uji unit sebelum menerapkannya ke lingkungan produksi.
Scala
Untuk menguji src/main/scala/example/MyUDF.scala, buat file pengujian di 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)
}
}
Tambahkan dependensi pengujian ke build.sbt:
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.15" % Test
Untuk menjalankan pengujian:
sbt test
Java
Untuk menguji src/main/java/com/example/MyUDF.java, buat file pengujian di 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));
}
}
Tambahkan dependensi JUnit ke bagian <dependencies> di pom.xml Anda:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
Untuk menjalankan pengujian:
mvn test