Python UDF 설계하기

이 항목은 Python UDF를 설계하는 데 도움이 됩니다.

이 항목의 내용:

참고

Python UDF 배치 API가 있으며, 이를 사용하면 입력 행 배치를 Pandas DataFrames 로 수신하고 결과 배치를 Pandas 배열 또는 Series 로 반환하는 Python 함수를 정의할 수 있습니다. 배치 인터페이스를 사용하면 머신 러닝 추론 시나리오에서 훨씬 더 나은 성능을 얻을 수 있습니다. 자세한 내용은 Python UDF Batch API 섹션을 참조하십시오.

데이터 타입 선택

코드를 작성하기 전에:

  • 함수가 인자로 수락해야 하는 데이터 타입, 그리고 함수가 반환해야 하는 데이터 타입을 선택하십시오.

  • 타임존 관련 문제를 고려하십시오.

  • NULL 값을 처리하는 방법을 결정하십시오.

매개 변수 및 반환 형식에 대한 SQL-Python 데이터 타입 매핑

아래 표는SQL과 Python 간의 형식 매핑을 보여줍니다. 이러한 매핑은 일반적으로 Python UDF에 전달된 인자, 그리고 UDF에서 반환된 값 모두에 적용됩니다.

SQL 형식

Python 형식

참고

NUMBER

int 또는 decimal.Decimal

NUMBER 형식의 소수 자릿수가 0이면 int Python 형식이 사용됩니다. 그렇지 않으면 decimal.Decimal 형식이 사용됩니다.

FLOAT

float

부동 소수점 연산에는 특히 집계 함수가 많은 수의 행을 처리할 때 작은 반올림 오차가 누적될 수 있습니다. 행이 다른 순서로 처리되는 경우 쿼리가 실행될 때마다 반올림 오차가 달라질 수 있습니다. 자세한 내용은 숫자 데이터 타입: 부동 소수점 을 참조하십시오.

VARCHAR

str

BINARY

bytes

BOOLEAN

bool

DATE

datetime.date

TIME

datetime.time

Snowflake는 나노초 정밀도로 시간 값을 저장할 수 있지만, Python datetime.time 형식은 밀리초 정밀도만 유지합니다. Snowflake와 Python 데이터 타입 간의 변환은 유효 정밀도를 밀리초로 줄일 수 있습니다.

TIMESTAMP_LTZ

datetime.datetime

현지 타임존을 사용하여 내부 UTC 시간을 현지 《naive》 날짜/시간으로 변환합니다. 반환 형식으로 《naive》 날짜/시간이 필요합니다.

TIMESTAMP_NTZ

datetime.datetime

《naive》 날짜/시간으로 직접 변환합니다. 반환 형식으로 《naive》 날짜/시간이 필요합니다.

TIMESTAMP_TZ

datetime.datetime

타임존 정보가 있는 《aware》 날짜/시간으로 변환합니다. 반환 형식으로 《aware》 날짜/시간이 필요합니다.

VARIANT

dict, list, int, float, str 또는 bool

각 베리언트 행은 인자에 대해 동적으로 Python 형식으로 변환되고 반환 값에 대해서는 그 반대로 변환됩니다. decimal, binary, date, time, timestamp_ltz, timestamp_ntz, timestamp_tz 형식은 기본 Python 형식이 아닌 문자열로 변환됩니다. Python 데이터 타입이 VARIANT로 변환될 때 포함된 Python 10진수 데이터가 있는 경우 포함된 Python 10진수는 VARIANT에서 String으로 변환됩니다.

OBJECT

dict

Python 데이터 타입이 OBJECT로 변환될 때 포함된 Python 10진수 데이터가 있는 경우 포함된 Python 10진수는 OBJECT에서 String으로 변환됩니다.

ARRAY

list

Python 데이터 타입이 ARRAY로 변환될 때 포함된 Python 10진수 데이터가 있는 경우 포함된 Python 10진수는 ARRAY에서 String으로 변환됩니다.

GEOGRAPHY

dict

지리 형식을 GeoJSON 으로 지정한 다음 Python dict로 변환합니다.

TIMESTAMP_LTZ 값 및 타임존

Python UDF는 호출되는 환경과 크게 분리되어 있습니다. 그러나 타임존은 호출 환경에서 상속됩니다. 호출자의 세션이 Python UDF 호출 전에 기본 타임존을 설정한 경우, Python UDF는 동일한 기본 타임존을 갖습니다. 타임존에 대한 자세한 내용은 TIMEZONE 섹션을 참조하십시오.

NULL 값

Variant를 제외한 모든 Snowflake 형식의 경우 Python UDF에 대한 SQL NULL 인자는 Python None 값으로 변환되고 반환된 Python None 값은 다시 SQL NULL 로 변환됩니다.

Variant 형식 값은 SQL NULL 또는 VARIANT JSON null 일 수 있습니다. Snowflake VARIANT NULL에 대한 자세한 내용은 NULL 값 을 참조하십시오.

  • VARIANT JSON null 은 Python None 으로 변환됩니다.

  • SQL NULLis_sql_null 속성이 있는 Python 오브젝트로 변환됩니다.

예를 들어 Python UDF에서의 NULL 처리 를 참조하십시오.

Snowflake에서 부과한 제약 조건 내에서 유지되는 Python UDF 설계하기

Snowflake 환경 내에서 안정성을 보장하기 위해 Snowflake는 Python UDF에 다음과 같은 제약 조건을 적용합니다. 달리 명시되지 않는 한 이러한 제한 사항은 UDF가 만들어질 때가 아니라 실행될 때 적용됩니다.

머신 러닝(Z) 모델 학습은 때로는 리소스를 매우 많이 사용할 수 있습니다. Snowpark에 최적화된 웨어하우스는 대량의 메모리와 컴퓨팅 리소스가 필요한 워크로드에 사용할 수 있는 일종의 Snowflake 가상 웨어하우스입니다. 머신 러닝 모델과 Snowpark Python에 대한 자세한 내용은 Snowpark Python으로 머신 러닝 모델 학습시키기 섹션을 참조하십시오.

메모리

너무 많은 메모리를 사용하지 마십시오.

  • 큰 데이터 값은 많은 양의 메모리를 사용할 수 있습니다.

  • 과도한 스택 깊이는 많은 양의 메모리를 사용할 수 있습니다.

UDF는 메모리를 너무 많이 사용하는 경우 오류를 반환합니다. 특정 제한은 변경될 수 있습니다.

너무 많은 메모리를 사용하여 UDF가 실패하는 경우 Snowpark에 최적화된 웨어하우스 를 사용해 보십시오.

시간

호출당 많은 시간이 소요되는 알고리즘을 피하십시오.

UDF를 완료하는 데 너무 오래 걸리는 경우, Snowflake는 SQL 문을 종료하고 사용자에게 오류를 반환합니다. 이는 무한 루프와 같은 오류의 영향과 비용을 제한합니다.

모듈 설계하기

SQL 문이 Python UDF를 호출하면 Snowflake는 사용자가 작성한 Python 함수를 호출합니다. Python 함수를 《핸들러 함수》 또는 줄여서 《핸들러》라고 합니다. 핸들러는 사용자 제공 모듈 내부에 구현된 함수입니다.

모든 Python 함수와 마찬가지로 함수는 모듈의 일부로 선언되어야 합니다.

핸들러는 Python UDF에 전달된 각 행에 대해 한 번 호출됩니다. 해당 함수를 포함하는 모듈은 각 행에 대해 다시 가져오지 않습니다. Snowflake는 동일한 모듈의 핸들러 함수를 두 번 이상 호출할 수 있습니다.

코드 실행을 최적화하기 위해 Snowflake는 초기화가 느릴 수 있는 반면 핸들러 함수의 실행은 빠르다고 가정합니다. Snowflake는 핸들러를 실행하는 것(한 행의 입력으로 핸들러를 호출하는 시간)보다 초기화 실행(UDF를 로딩하는 시간과 모듈을 초기화하는 시간 포함)에 더 긴 시간 제한을 설정합니다.

모듈 설계에 대한 추가 정보는 Python UDF 만들기 에 있습니다.

스칼라 UDF에서 초기화 최적화 및 전역 상태 제어

대부분의 스칼라 UDF는 아래 지침을 따라야 합니다.

  • 행 간에 변경되지 않는 공유 상태를 초기화해야 하는 경우, 핸들러 함수 대신 모듈에서 초기화하십시오.

  • 스레드로부터 안전하도록 핸들러 함수를 작성하십시오.

  • 행 간에 동적 상태를 저장 및 공유하지 마십시오.

UDF가 이러한 지침을 따를 수 없는 경우 Snowflake는 스칼라 UDF가 독립적으로 처리될 것으로 예상합니다. 호출 간에 공유되는 상태에 의존하면 예기치 않은 동작이 발생할 수 있습니다. 시스템이 임의의 순서로 행을 처리하고 이러한 호출을 여러 인스턴스에 분산할 수 있기 때문입니다. 또한 여러 스레드의 동일한 Python 인터프리터 내에서 같은 핸들러 함수가 여러 번 실행될 수 있습니다.

UDF는 핸들러 함수에 대한 호출에서 공유 상태에 의존하는 것을 피해야 합니다. 그러나 사용자가 UDF에서 공유 상태를 저장하려 할 수 있는 다음 두 가지 상황이 있습니다.

  • 각 행에 대해 반복하고 싶지 않은, 부담이 큰 초기화 논리가 포함된 코드입니다.

  • 캐시와 같이 행 간에 공유 상태를 활용하는 코드입니다.

여러 핸들러 호출에서 공유할 전역 상태를 유지해야 할 때는 스레딩 - 스레드 기반 병렬 처리 에 설명된 동기화 기본 요소를 사용하여 데이터 경합으로부터 전역 상태를 보호해야 합니다.

규모와 성능 최적화하기

데이터 과학 라이브러리와 함께 Python 배치 API 사용하기

코드에서 머신 러닝 또는 데이터 과학 라이브러리를 사용할 때 Python UDF 배치 API를 사용하십시오. 배치 API를 사용하면 이러한 라이브러리가 작동하도록 최적화된 배치로 입력 행을 수신하는 Python 함수를 정의할 수 있습니다.

자세한 내용은 Python UDF Batch API 섹션을 참조하십시오.

단일 스레드 UDF 핸들러 작성하기

단일 스레드로 된 UDF 핸들러를 작성합니다. Snowflake는 가상 웨어하우스 컴퓨팅 리소스 전반에 걸쳐 데이터 분할과 UDF 확장을 처리합니다.

모듈에 값비싼 초기화 코드 넣기

모듈 범위에 값비싼 초기화 코드를 넣습니다. 그러면 UDF가 초기화될 때 코드가 한 번 수행됩니다. 모든 UDF 핸들러 호출에서 값비싼 초기화 코드를 다시 실행하지 마십시오.

오류 처리

UDF로 사용되는 Python 함수는 일반 Python 예외 처리 기술을 사용하여 함수 내에서 오류를 포착할 수 있습니다.

함수 내에서 예외가 발생하고 이 예외가 함수에서 포착되지 않으면 Snowflake는 예외에 대한 스택 추적을 포함하는 오류를 발생시킵니다.

쿼리를 종료하고 SQL 오류를 생성하기 위해 예외를 포착하지 않고 명시적으로 예외를 발생시킬 수 있습니다. 예:

if (x < 0):
  raise ValueError("x must be non-negative.");

디버깅할 때 SQL 오류 메시지 텍스트에 값을 포함할 수 있습니다. 이렇게 하려면 전체 Python 함수 본문을 try-catch 블록에 배치하고, 포착된 오류 메시지에 인자 값을 추가하고, 확장된 메시지와 함께 예외가 발생하도록 하십시오. 민감한 데이터가 노출되지 않도록 하려면 프로덕션 환경에 배포하기 전에 인자 값을 제거하십시오.

우수한 보안 관행 따르기

  • 함수(및 호출하는 모든 라이브러리 함수)는 순수 함수로 작동해야 하며, 수신한 데이터에 대해서만 작동하고 해당 데이터를 기반으로 값을 반환해야 하며, 이 과정에서 부작용 없이 작동해야 합니다. 코드는 적절한 양의 메모리와 프로세서 시간을 사용하는 것 외에는 기본 시스템의 상태에 영향을 미치려고 해서는 안 됩니다.

  • Python UDF는 제한된 엔진 내에서 실행됩니다. 코드 또는 사용하는 라이브러리 함수의 코드는 다음을 포함하여 금지된 시스템 호출을 사용해서는 안 됩니다.

    • 프로세스 제어. 예를 들어, 프로세스를 포크할 수 없습니다. (단, 여러 스레드는 사용할 수 있습니다.)

    • 파일 시스템 액세스.

      다음 예외를 제외하고 Python UDF는 파일을 읽거나 쓰지 않아야 합니다.

      • Python UDF는 CREATE FUNCTION 명령의 imports 절에 지정된 파일을 읽을 수 있습니다.

        자세한 내용은 UDF 핸들러로 파일 읽기 섹션을 참조하십시오. 예를 보려면 스테이지에서 Python UDF로 파일 로딩하기 를 참조하십시오.

      • Python UDF는 로그 파일과 같은 파일을 /tmp 디렉터리에 쓸 수 있습니다.

        각 쿼리는 고유한 /tmp가 저장되는 자체 메모리 지원 파일 시스템을 가져오므로 서로 다른 쿼리에는 파일 이름 충돌이 있을 수 없습니다. 그러나 단일 쿼리가 둘 이상의 UDF를 호출하고 이러한 UDF가 동일 파일 이름에 쓰려고 하면 쿼리 내 충돌이 발생할 수 있습니다. 또한 Python UDF는 별개의 작업자 프로세스에서 병렬로 실행될 수 있으므로 /tmp 디렉터리에 쓸 때 주의해야 합니다.

        파일 쓰기에 대한 자세한 내용은 UDF 핸들러로 파일 쓰기 섹션을 참조하십시오. 예를 보려면 스테이징된 파일의 압축 풀기 를 참조하십시오.

    • 네트워크 액세스.

      참고

      코드는 네트워크에 직접 또는 간접적으로 액세스할 수 없으므로 Snowflake Python Connector 드라이버의 코드를 사용하여 데이터베이스에 액세스할 수 없습니다. UDF 자체는 Snowflake의 클라이언트 역할을 할 수 없습니다.

맨 위로 이동