Important
Scala および Java UDF は、ガバナンス、再利用、および検出可能性のために Unity カタログに登録できます。 Unity カタログの Scala と Java ユーザー定義関数 (UDF) を参照してください。
このページでは、セッション スコープの Scala と Java UDF を Azure Databricks で作成する方法について説明します。 セッション スコープ UDF はノートブックまたはジョブで定義され、現在の SparkSession にのみ適用されます。 SQL 言語リファレンスについては、 外部ユーザー定義スカラー関数 (UDF) を参照してください。
アプローチを選択する
Scala または Java UDF は、次の方法で定義できます。 言語、ガバナンス、コンピューティング間ですべての UDF の種類を比較するには、 Unity カタログのガバナンスとセッション スコープの UDF を参照してください。
| Approach | 説明 |
|---|---|
| インライン Scala UDF | Scala 関数またはラムダを使用して、ノートブックで UDF を定義します。 セッション単位。 サーバーレス コンピューティングではサポートされていません。 |
| JAR から UDF をJavaする |
spark.udf.registerJavaFunctionを使用して、JAR からプリコンパイル済み UDF クラスを登録します。 セッション単位。 サーバーレス コンピューティングでサポートされます。 |
| Unity Catalog 管理下の Scala または Java UDF | ガバナンス、再利用、および検出可能性のために、UNITY カタログに UDF を登録します。 サーバーレス コンピューティングでサポートされます。 |
Requirements
- 標準アクセス モードの Unity カタログ対応コンピューティング上の Scala UDF には、Databricks Runtime 14.2 以降が必要です。
- Unity カタログ対応クラスターでの Scala UDF の ARM インスタンスのサポートには、Databricks Runtime 15.2 以降が必要です。
- JAR から Java UDF を
spark.udf.registerJavaFunctionに登録するには、Databricks Runtime 18 LTS 以降が必要です。 JAR からのJava UDF の登録を参照してください。
Important
JAR を実行するコンピューティングと同じ Scala バージョンと Apache Spark バージョンに対して JAR をビルドします。 一致しない場合、登録時または呼び出し時に UDF が失敗する可能性があります。
- クラシック コンピューティング: ご利用の Databricks Runtime バージョンの Scala と Spark のバージョンと一致します。 お使いのバージョンのDatabricks Runtime リリース ノートのバージョンと互換性のシステム環境セクションを参照してください。 たとえば、Databricks Runtime 18 LTS では Scala 2.13.16 と Apache Spark 4.0 が使用されます。
- サーバーレス コンピューティング: 環境バージョンの Scala バージョンと一致します。 「サーバーレス環境のバージョン」を参照してください。
Apache Spark の依存関係を provided としてマークして、JAR にバンドルされないようにします。 UDF が使用するサード パーティの依存関係のみを含めます。
関数を UDF として登録する
spark.udf.registerを使用して Scala 関数を UDF として登録します。
val squared = (s: Long) => {
s * s
}
spark.udf.register("square", squared)
Spark SQL で UDF を呼び出す
一時ビューを作成し、SQL クエリで UDF を呼び出します。
spark.range(1, 20).createOrReplaceTempView("test")
%sql select id, square(id) as id_squared from test
DataFrame で UDF を使用する
DataFrame API を使用して UDF を呼び出すこともできます。
import org.apache.spark.sql.functions.{col, udf}
val squared = udf((s: Long) => s * s)
display(spark.range(1, 20).select(squared(col("id")) as "id_squared"))
UDFを含むファイル
Important
この機能は ベータ版です。 ワークスペース管理者は、[ プレビュー] ページからこの機能へのアクセスを制御できます。 Manage Azure Databricks プレビューを参照してください。
ファイルのScalaタイプは FileRefです。 UDFのパラメータまたはリターン型として、トップレベル型または入れ子型として使用できます。 タイプとその入れ子規則については、 FILE 型を参照してください。
UDFでファイルの内容を読み取るには、 FileRef上で以下のいずれかを呼び出します。
-
asLocalFile():はパスを受け入れるライブラリに渡せるjava.io.Fileを返します。 -
open()ファイルのバイトを読み取るためのjava.io.InputStreamを返します。 電話をかけた人が閉じる。
UDFで新しい FileRef を生成するには、以下のいずれかの静的メソッドを呼び出します。
-
FileRef.create(uri):uriのファイルへの参照を作成します。 -
FileRef.fromBytes(bytes, destinationPath, contentType):bytesを外部ファイルとしてdestinationPathボリュームパスにアップロードし、参照を返します。 -
FileRef.fromLocalFile(localFile, destinationPath, contentType): ローカルファイルをdestinationPathボリュームパスに外部ファイルとしてアップロードし、参照を返します。
FILE MANAGEDコラムに書くUDFからのFileRef返却はサポートされていません。
Python、Scala、SQLの例として、画像処理、ファイルタイプ検出、ビデオフレーム抽出については、UDFを持つプロセスファイルを参照してください。
JAR からJava UDF を登録する
UDF を JAR としてパッケージ化し、 spark.addArtifactを使用してセッションに追加し、UDF クラスを spark.udf.registerJavaFunction に登録します。
注
Databricks Runtime 18 LTS 以降の標準アクセス モードとサーバーレス コンピューティングでサポートされます。 登録された関数はセッション スコープであり、Unity カタログには登録されません。
次の手順では、プロジェクトの作成、UDF クラスの記述、Fat JAR の構築、および登録について説明します。
手順 1: プロジェクトを作成する
Scala または Java でプロジェクトを設定します。
Scala
sbtを使用して新しい Scala プロジェクトを作成します。
sbt new scala/scala-seed.g8
build.sbt ファイルの内容を次の内容に置き換えます。 コンピューティングに合わせて scalaVersion と spark-sql バージョンを設定します。
scalaVersion := "2.13.16"
ThisBuild / organization := "com.example"
lazy val myUDF = (project in file("."))
.settings(
name := "my-udf",
libraryDependencies += "org.apache.spark" %% "spark-sql" % "4.0.0" % "provided"
)
sbt-assembly プラグインを有効にして、fat JAR をビルドします。
project/assembly.sbtを作成または編集し、次の項目を追加します。
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.0.0")
Java
クイック スタートアーキタイプを使用して新しい Maven プロジェクトを作成します。
mvn archetype:generate \
-DgroupId=com.example \
-DartifactId=my-udf \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=false
このコマンドは、 src/main/java ディレクトリと src/test/java ディレクトリを使用して、標準の Maven プロジェクト構造を作成します。
生成された pom.xmlで、 <project></project> タグ内に <properties> ブロックを追加し、fat JAR を構築するように maven-shade-plugin を構成します。
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<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>
手順 2: UDF クラスを記述する
UDF クラスは、(org.apache.spark.sql.api.java.UDFを介してUDF1) UDF22 インターフェイスのいずれかを実装する必要があります。この数値は、UDF が受け取る入力引数の数を示します。 ロジックを使用して call() メソッドを実装します。
ハンドラーは、Java クラスである必要があります。
spark.udf.registerJavaFunction はリフレクションによってクラスを読み込むため、パブリック引数なしのコンストラクターを持つ最上位 (または入れ子になった static ) パブリック クラスである必要があります。 Scala class または object はこの要件を満たしていないため、呼び出し時に失敗します。 sbt を使用して JAR をビルドできますが、UDF クラス自体をJavaで記述する必要があります。
src/main/java/com/example/MyIntegerUDF.javaを作成します。
package com.example;
import org.apache.spark.sql.api.java.UDF1;
public class MyIntegerUDF implements UDF1<Integer, Integer> {
@Override
public Integer call(Integer x) {
return x + 1;
}
}
手順 3: fat JAR を作成する
コンパイル済みの UDF を Fat JAR にパッケージ化します。
Scala
プロジェクトのルート ディレクトリから、次のコマンドを実行します。
sbt clean assembly
fat JAR は、target/scala-2.13/ に my-udf-assembly-0.1.0-SNAPSHOT.jar のような名前で作成されます。
Java
プロジェクトのルート ディレクトリから、次のコマンドを実行します。
mvn clean package
fat JAR は、target/ に my-udf-1.0-SNAPSHOT.jar のような名前で作成されます。
手順 4: JAR を Unity カタログ ボリュームにアップロードする
コンピューティングからアクセスできるように、 JAR を Unity カタログ ボリューム にアップロードします。 まだボリュームがない場合は、作成してください:
CREATE VOLUME IF NOT EXISTS my_catalog.my_schema.udf_jars
COMMENT 'Storage for UDF JAR files';
カタログ エクスプローラーを使用して JAR ファイルをボリュームにアップロードします。
- Azure Databricks ワークスペースで、
Catalog をクリックしてカタログ エクスプローラーを開きます。
- カタログを選択し、次にボリュームを含むスキーマを選択します。
- ボリューム名をクリックします。
- [ このボリュームにアップロード] をクリックし、JAR ファイルを選択します。
- アップロードをクリックします。
- アップロードが完了したら、JAR ファイルの名前をクリックし、[ パスのコピー ] をクリックしてボリューム パスをコピーします。 たとえば、「
/Volumes/my_catalog/my_schema/udf_jars/my-udf-assembly-0.1.0-SNAPSHOT.jar」のように入力します。 このパスは、次の手順で必要になります。
手順 5: UDF を登録して呼び出す
ボリューム パスを使用してセッションに JAR を追加し、UDF クラスを登録して、Spark SQL から呼び出します。
# Add the JAR containing your UDF class to the session
spark.addArtifact("/Volumes/my_catalog/my_schema/udf_jars/my-udf-assembly-0.1.0-SNAPSHOT.jar")
# Register the UDF class, providing the SQL function name,
# the fully qualified class name, and the return type
from pyspark.sql.types import IntegerType
spark.udf.registerJavaFunction(
"my_udf",
"com.example.MyIntegerUDF",
IntegerType(),
)
# Call the UDF from Spark SQL
spark.sql("SELECT my_udf(21)").show()
サーバーレスおよび標準アクセス モードのコンピューティングでは、明示的な戻り値の型を渡す必要があります。 戻り値の型を省略すると、 UC_COMMAND_NOT_SUPPORTED_IN_SHARED_ACCESS_MODEで失敗します。 ユーザー定義集計関数 (UDF) は、 registerJavaFunctionではサポートされていません。
このクエリは UDF 出力を返し、関数が登録され、呼び出し可能であることを確認します。
+----------+
| my_udf(21)|
+----------+
| 22|
+----------+
評価順序と null チェック
Spark SQL (SQL、DataFrame、Dataset API を含む) では、部分式の評価の順序は保証されません。 Spark は、演算子または関数の入力を左から右に評価しません。 論理 AND 式と OR 式には、左から右への短絡評価のセマンティクスはありません。
ブール式の副作用や評価順序、または WHERE 句と HAVING 句の順序に依存しないでください。 クエリ オプティマイザーでは、これらの式と句を並べ替えることができます。 UDF が NULL チェックのためにショートサーキット セマンティクスに依存している場合、Spark では UDF の前に null チェックが実行されることを保証しません。 例えば次が挙げられます。
spark.udf.register("strlen", (s: String) => s.length)
spark.sql("select s from test1 where s is not null and strlen(s) > 1") // no guarantee
この WHERE 句では、spark が null を除外した後に strlen UDF を呼び出す保証はありません。
Null チェックを処理するために、Databricks では次のいずれかをお勧めします。
- UDF 自体を null に対応させ、UDF 内で null チェックを実行する
-
IFまたはCASE WHEN式を使用して null チェックを実行し、条件分岐で UDF を呼び出す
spark.udf.register("strlen_nullsafe", (s: String) => if (s != null) s.length else -1)
spark.sql("select s from test1 where s is not null and strlen_nullsafe(s) > 1") // ok
spark.sql("select s from test1 where if(s is not null, strlen(s), null) > 1") // ok
型付きデータセットAPI
注
この機能は、Databricks Runtime 15.4 以降の標準アクセス モードの Unity カタログ対応クラスターでサポートされています。
型指定されたデータセット API を使用して、ユーザー定義関数を使用してデータセットに対してマップ、フィルター、集計などの変換を実行します。
次の例では、 map() API を使用して、結果列の数値をプレフィックス付きの文字列に変更します。
spark.range(3).map(f => s"row-$f").show()
この例では map()を使用していますが、 filter()、 mapPartitions()、 foreach()、 foreachPartition()、 reduce()、 flatMap()など、他の型指定されたデータセット API にも同じパターンが適用されます。
Scala UDF の機能と Databricks ランタイムの互換性
次の機能では、標準 (共有) アクセス モードの Unity カタログ対応クラスターで Databricks Runtime の最小バージョンが必要です。
| 特徴 | Databricks の最低ランタイム バージョン |
|---|---|
| スカラーUDF(ユーザー定義関数) | Databricks Runtime(データブリックス ランタイム)14.2 |
Dataset.map、 Dataset.mapPartitions、 Dataset.filter、 Dataset.reduce、 Dataset.flatMap |
Databricks ランタイム 15.4 |
KeyValueGroupedDataset.flatMapGroups、KeyValueGroupedDataset.mapGroups |
Databricks ランタイム 15.4 |
(ストリーミング) foreachWriter Sink |
Databricks ランタイム 15.4 |
(ストリーミング) foreachBatch |
Databricks ランタイム 16.1 |
(ストリーミング) KeyValueGroupedDataset.flatMapGroupsWithState |
Databricks ランタイム 16.2 |
spark.udf.registerJavaFunction(JAR 内の Java UDF) |
Databricks Runtime 18 LTS |