スカラーScala UDFs でのグローバル状態の制御¶
共有状態へのアクセスを必要とする UDF とハンドラーを設計するときは、Snowflakeが UDFs を実行して行を処理する方法を考慮する必要があります。
ほとんどのハンドラーは次のガイドラインに従う必要があります。
行全般で変化しない共有状態を初期化する必要がある場合は、ハンドラー関数の外部(コンストラクターなど)で初期化します。
スレッドセーフになるようにハンドラーメソッドを記述します。
行間で動的状態を保存および共有することは避けてください。
UDF がこれらのガイドラインに従えない場合、またはこれらのガイドラインの理由をより深く理解したい場合は、次のいくつかのサブセクションをお読みください。
並列化の理解¶
パフォーマンスを向上させるために、Snowflakeは JVMs の全体および内部で並列化します。
JVMs 全体の並列化¶
Snowflakeは、 ウェアハウス のワーカー間で並列化されます。各ワーカーは1つ(または複数)の JVMs を実行します。これは、グローバル共有状態がないことを意味します。最大で、状態は単一の JVM 内でのみ共有できます。
JVMs 内部での並列化¶
各 JVM は、同じインスタンスのハンドラーメソッドを並行して呼び出すことができる複数のスレッドを実行できます。これは、各ハンドラーメソッドがスレッドセーフである必要があることを意味します。
UDF が IMMUTABLE で、 SQL ステートメントが同じ行に対して同じ引数を使用して UDF を複数回呼び出す場合、 UDF は、その行の呼び出しごとに同じ値を返します。
たとえば、 UDF が IMMUTABLE の場合、以下は各行に対して同じ値を2回返します。
SELECT my_scala_udf(42), my_scala_udf(42) FROM table1;同じ引数が渡された場合でも複数の呼び出しで独立した値を返すようにし、関数 VOLATILE を宣言しない場合は、複数の個別の UDFs を同じハンドラーメソッドにバインドします。
次のステップを使用してこれを実行できます。
次のコードを使用して、
@udf_libs/rand.jar
という名前の JAR ファイルを作成します。class MyClass { var x: Double = 0.0 // Constructor def this() = { x = Math.random() } // Handler def myHandler(): Double = x }以下に示すように、Scala UDFs を作成します。
これらの UDFs の名前は異なりますが、同じ JAR ファイルと、その JAR ファイル内の同じハンドラーを使用します。
CREATE FUNCTION my_scala_udf_1() RETURNS DOUBLE LANGUAGE SCALA IMPORTS = ('@udf_libs/rand.jar') HANDLER = 'MyClass.myHandler'; CREATE FUNCTION my_scala_udf_2() RETURNS DOUBLE LANGUAGE SCALA IMPORTS = ('@udf_libs/rand.jar') HANDLER = 'MyClass.myHandler';次のコードを使用して、両方の UDFs を呼び出します。
UDFs は、同じ JAR ファイルとハンドラーをポイントします。これらの呼び出しは、同じクラスのインスタンスを2つ作成します。各インスタンスは独立した値を返すため、以下の例では、同じ値を2回返すのではなく、2つの独立した値を返します。
SELECT my_scala_udf_1(), my_scala_udf_2() FROM table1;
JVM 状態情報の保存¶
動的共有状態に依存しない理由の1つは、行が必ずしも予測可能な順序で処理されるとは限らないことです。Snowflakeは、 SQL ステートメントが実行されるたびに、バッチの数、バッチが処理される順序、およびバッチ内の行の順序を変更できます。1つの行が後続の行の戻り値に影響を与えるようにスカラー UDF が設計されている場合、 UDF は、 UDF が実行されるたびに異なる結果を返す可能性があります。