스칼라 Scala UDF에서 전역 상태 제어하기¶
공유 상태에 대한 액세스가 필요한 UDF와 처리기를 설계할 때 Snowflake가 UDF를 실행하여 행을 처리하는 방식을 설명해야 합니다.
대부분의 처리기는 다음 지침을 따라야 합니다.
행 간에 변경되지 않는 공유 상태를 초기화해야 하는 경우 처리기 함수 외부에서(예: 생성자에서) 초기화하십시오.
스레드로부터 안전하도록 핸들러 메서드를 작성하십시오.
행 간에 동적 상태를 저장 및 공유하지 마십시오.
UDF가 이러한 지침을 따를 수 없거나 이러한 지침의 이유를 더 깊이 이해하려는 경우, 다음 몇 가지 하위 섹션을 읽으십시오.
병렬 처리 이해하기¶
성능을 향상시키기 위해 Snowflake는 JVM 전체에 걸쳐, 그리고 JVM 내에서 병렬 처리를 합니다.
JVM 전체에 걸쳐 병렬 처리하기¶
Snowflake는 웨어하우스 의 작업자 전체에 걸쳐 병렬 처리를 합니다. 각 작업자는 하나 이상의 JVM을 실행합니다. 이는 전역 공유 상태가 없음을 의미합니다. 기껏해야 단일 JVM 내에서만 상태를 공유할 수 있습니다.
JVM 내에서 병렬 처리하기¶
각 JVM은 동일한 인스턴스의 핸들러 메서드를 병렬로 호출할 수 있는 여러 스레드를 실행할 수 있습니다. 이는 각 핸들러 메서드가 스레드로부터 안전해야 함을 의미합니다.
UDF가 IMMUTABLE이고 SQL 문이 동일 행에 대해 동일 인자를 사용하여 UDF를 두 번 이상 호출하는 경우, UDF는 해당 행의 각 호출에 대해 동일 값을 반환합니다.
예를 들어, UDF가 IMMUTABLE인 경우 다음은 각 행에 대해 동일 값을 두 번 반환합니다.
SELECT my_scala_udf(42), my_scala_udf(42) FROM table1;동일 인자가 전달된 경우에도 여러 호출이 독립 값을 반환하고 함수 VOLATILE을 선언하지 않으려면 여러 개별 UDF를 동일 핸들러 메서드에 바인딩하십시오.
다음 단계에 따라 이 작업을 수행할 수 있습니다.
다음 코드를 사용하여
@udf_libs/rand.jar
라는 JAR 파일을 만듭니다.class MyClass { var x: Double = 0.0 // Constructor def this() = { x = Math.random() } // Handler def myHandler(): Double = x }아래와 같이 Scala UDF를 만듭니다.
이러한 UDF는 이름이 다르지만, 동일 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';다음 코드를 사용하여 두 UDF를 모두 호출합니다.
UDF는 동일한 JAR 파일 및 핸들러를 가리킵니다. 이러한 호출은 동일한 클래스의 두 인스턴스를 만듭니다. 각 인스턴스는 독립적인 값을 반환하므로 아래 예에서는 동일 값을 두 번 반환하는 대신 두 개의 독립적인 값을 반환합니다.
SELECT my_scala_udf_1(), my_scala_udf_2() FROM table1;
JVM 상태 정보 저장¶
동적 공유 상태에 의존하지 않는 한 가지 이유는 행이 반드시 예측 가능한 순서로 처리되는 것은 아니기 때문입니다. SQL 문이 실행될 때마다 Snowflake는 배치 수, 배치가 처리되는 순서, 배치 내의 행 순서를 변경할 수 있습니다. 한 행이 후속 행의 반환 값에 영향을 미치도록 스칼라 UDF가 설계된 경우, UDF는 UDF가 실행될 때마다 다른 결과를 반환할 수 있습니다.