セッションスコープの Scala および Java UDF

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 ファイルの内容を次の内容に置き換えます。 コンピューティングに合わせて scalaVersionspark-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 ファイルをボリュームにアップロードします。

  1. Azure Databricks ワークスペースで、Data icon.Catalog をクリックしてカタログ エクスプローラーを開きます。
  2. カタログを選択し、次にボリュームを含むスキーマを選択します。
  3. ボリューム名をクリックします。
  4. [ このボリュームにアップロード] をクリックし、JAR ファイルを選択します。
  5. アップロードをクリックします。
  6. アップロードが完了したら、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.mapDataset.mapPartitionsDataset.filterDataset.reduceDataset.flatMap Databricks ランタイム 15.4
KeyValueGroupedDataset.flatMapGroupsKeyValueGroupedDataset.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