주:

Oracle Big Data Service에서 Apache Impala 스크립트를 Trino로 마이그레이션

소개

Oracle Big Data Service(BDS)는 Oracle에서 제공하는 클라우드 기반 서비스로, 사용자가 Hadoop 클러스터, Spark 클러스터, Trino 및 기타 빅데이터 서비스를 생성하고 관리할 수 있도록 합니다. 빅 데이터 분석의 세계에서 Trino는 대용량 데이터 세트에서 대화형 분석 쿼리를 실행하도록 설계된 고성능 분산 SQL 쿼리 엔진입니다. Trino를 사용하면 통합 SQL 인터페이스 내에서 Hadoop 분산 파일 시스템(HDFS), OCI 버킷, 기존 관계형 데이터베이스 등 여러 소스에서 데이터를 쿼리할 수 있습니다. 대기 시간이 짧은 대규모 데이터를 처리할 수 있는 능력 덕분에 데이터 엔지니어와 분석가 모두에게 강력한 도구가 되었습니다.

최신 데이터 플랫폼으로 전환하면서 많은 조직이 Oracle Big Data Service의 Apache Impala에서 Trino로 마이그레이션하고 있습니다. 이 자습서에서는 Trino로의 원활한 마이그레이션을 위해 Apache Impala 스크립트를 자동화하고 수동으로 조정하는 단계별 접근 방식을 제공합니다.

목표

필요 조건

작업 1: 스크립트 변환 자동화

자동화된 스크립트는 Oracle Big Data Service의 Trino와 호환되도록 Apache Impala 스크립트를 변환하는 프로세스를 간소화하도록 설계되었습니다. 이 스크립트는 여러 가지 주요 변환을 자동화하여 필요한 수동 작업의 양을 줄이고 마이그레이션 프로세스 중 인적 오류가 발생할 가능성을 최소화합니다. 다음은 해당 기능에 대한 분석입니다.

  1. 파일 처리 및 초기화

    • 스크립트는 입력 .hql 파일(일반적으로 Apache Impala 질의에 사용됨)을 읽는 것으로 시작합니다.

    • 변환된 Trino 호환 질의가 저장될 새 출력 파일을 생성합니다.

    • 스크립트는 USE catalog_name.schema_name를 사용하여 카탈로그 및 데이터베이스 컨텍스트를 설정하는 행을 추가하여 쿼리가 Trino의 올바른 컨텍스트에서 실행되도록 하는 것으로 시작합니다.

  2. 자동화된 쿼리 변환.

    • 테이블 처리 겹쳐쓰기: 스크립트는 Apache Impala 스크립트에서 OVERWRITE TABLE 문을 찾고 해당 DELETE FROM 문을 바로 위에 삽입합니다. 이렇게 하면 Trino가 데이터 덮어쓰기를 처리하는 방식에 따라 새 데이터를 삽입하기 전에 테이블이 지워집니다.

    • 임팔라 관련 함수 교체: 스크립트는 여러 변형을 적용하여 Apache Impala 관련 구문 및 함수를 Trino 호환 구문으로 대체합니다. 예:

      • invalidate metadataREFRESH 명령은 Trino에서 필요하지 않으므로 주석 처리됩니다.

      • Trino의 데이터 유형과 일치하도록 string에서 varchar로 데이터 유형 변경이 수행됩니다.

      • Trino가 잘림을 다르게 처리하므로 Impala의 TRUNCATE TABLE 문은 DELETE FROM로 바뀝니다.

      • COMPUTE STATS는 Trino가 통계를 수집하는 방법을 반영하여 ANALYZE로 바뀝니다.

      • 다른 특정 변환에는 nvl, ifnullfrom_utc_timestamp와 같은 처리 함수가 포함되며, 이를 Trino의 구문 및 함수로 조정합니다.

      • nvl2 함수를 동등한 COALESCE 표현식으로 변환하는 중입니다.

      • 정규 표현식 일치(REGEXP)를 Trino의 REGEXP_LIKE로 수정하는 중입니다.

      • 날짜 및 시간 기록 함수를 조정하여 Trino의 구문에 맞춥니다.

    Script:

    #!/bin/bash
    # Function to apply transformations to an HQL file
    process_file() {
       local input_file="$1"
       local output_file="${input_file%.hql}_modified.hql"
       local diff_file="${input_file%.hql}_diff.sql"
    
       # Create the output file with the initial modification
       {
          echo "use catalog_name.schema_name;"
          cat "$input_file"
       } > "$output_file"
    
       # Insert DELETE FROM statements above OVERWRITE TABLE statements
       awk '
       BEGIN { IGNORECASE = 1 }
       {
          if (match($0, /OVERWRITE[[:space:]]+TABLE[[:space:]]+([^[:space:]]+)/, arr)) {
                print "DELETE FROM " arr[1] ";"
          }
          else if (match($0, /OVERWRITE[[:space:]]+([^[:space:]]+)/, arr)) {
          print "DELETE FROM " arr[1] ";"
          }
          print
       }' "$output_file" > temp_file && mv temp_file "$output_file"
    
       # Define replacements as array of tuples (search, replace)
       declare -a replacements=(
          "old_line1 new_line1"
          "old_line2 new_line2"
          # Add more replacements as needed
       )
    
       # Apply replacements
       for replacement in "${replacements[@]}"; do
          search_text=$(echo "$replacement" | awk '{print $1}')
          replace_text=$(echo "$replacement" | awk '{print $2}')
          sed -i "s/${search_text}/${replace_text}/Ig" "$output_file"
       done
    
       # Handle specific replacements
       sed -i 's/invalidate metadata/--invalidate metadata/Ig' "$output_file"
       sed -i 's/REFRESH/--REFRESH/Ig' "$output_file"
       sed -i 's/ string/ varchar/Ig' "$output_file"
       sed -i 's/TRUNCATE[[:space:]]\+TABLE[[:space:]]*/DELETE FROM /Ig' "$output_file"
       sed -i 's/TRUNCATE /DELETE FROM /Ig' "$output_file"
       sed -i 's/OVERWRITE[[:space:]]\+TABLE[[:space:]]*/INTO /Ig' "$output_file"
       sed -i 's/COMPUTE[[:space:]]\+stats/ANALYZE/Ig' "$output_file"
       sed -i 's/INSERT[[:space:]]\+INTO[[:space:]]\+TABLE/INSERT INTO/Ig' "$output_file"
       sed -i 's/from_utc_timestamp(now(),/current_timestamp AT TIME ZONE /Ig' "$output_file"
       sed -i "s/'America\/New_York')/'America\/New_York'/Ig" "$output_file"
       sed -i 's|/\* +NOSHUFFLE \*/| |Ig' "$output_file"
       sed -i 's/into[[:space:]]\+table/into/Ig' "$output_file"
       sed -i 's/STRAIGHT_JOIN/ /Ig' "$output_file"
       sed -i 's/OVERWRITE[[:space:]]\+/INTO /Ig' "$output_file"
       sed -i 's/TABLE[[:space:]]\+INTO/INTO/Ig' "$output_file"
       sed -i 's/SET[[:space:]]\+DECIMAL_V2/--SET DECIMAL_V2/Ig' "$output_file"
       sed -i 's/nvl(/COALESCE(/Ig' "$output_file"
       sed -i 's/ifnull(/COALESCE(/Ig' "$output_file"
       sed -i 's/CREATE[[:space:]]\+EXTERNAL/CREATE/Ig' "$output_file"
       sed -i -E 's/TRUNC[[:space:]]*\(/TRUNCATE(/Ig' "$output_file"
       sed -i -E 's/CAST[[:space:]]*\(/TRY_CAST(/Ig' "$output_file"
       sed -i -E 's/instr[[:space:]]*\(/strpos(/Ig' "$output_file"
       sed -i 's/""[[:space:]]\+as/null as/Ig' "$output_file"
       sed -i -E 's/\$\{var\:([^}]*)\}/\$\{\1\}/Ig' "$output_file"
       sed -i -E 's/([a-zA-Z_][a-zA-Z0-9_]*)[[:space:]]+REGEXP[[:space:]]+('\''[^'\'']*'\'')/REGEXP_LIKE(\1, \2)/Ig' "$output_file"
       sed -i -E 's/lower\(([a-zA-Z_][a-zA-Z0-9_]*\))[[:space:]]+REGEXP[[:space:]]+('\''[^'\'']*'\'')/REGEXP_LIKE(lower(\1, \2)/Ig' "$output_file"
       sed -i "s/)\s*STORED\s*AS\s*PARQUET\s*LOCATION\s*'\([^']*\)';/) WITH (\nexternal_location = '\1',\nformat = 'PARQUET'\n);/Ig" "$output_file"    
       sed -i -E "s/try_cast\(extract\(([a-zA-Z0-9_.]+), 'MONTH'\) AS varchar\)/try_cast(extract(MONTH from try_cast(\1 AS timestamp)) as VARCHAR)/Ig" "$output_file"
       sed -i -E "s/TRY_CAST\(extract\(add_months\(([^,]+),([^,]+)\),[[:space:]]*'MONTH'\)[[:space:]]*AS[[:space:]]*varchar\)/TRY_CAST\(extract\(MONTH from date_add\('month',\2,try_cast\(\1 as timestamp\)\)\) AS varchar\)/Ig" "$output_file"
       sed -Ei "s/try_cast\(ADD_MONTHS\(([^,]+),\s*-?\s*([0-9]+)\) AS varchar\)/try_cast\(date_add\('month', -\2, try_cast(\1 as timestamp)\) AS varchar\)/Ig" "$output_file"
       sed -i -E 's/DATEDIFF\(/date_diff\('\''day'\'', /Ig' "$output_file"
       sed -i -E 's/TO_DATE\(/DATE\(/Ig' "$output_file"
       sed -i -E 's/now\(\)/current_timestamp/Ig' "$output_file"
    
       # Generate the diff file
       diff -u "$input_file" "$output_file" > "$diff_file"
    
       echo "Processed $input_file -> $output_file. Diff file created: $diff_file"
    }
    
    # Check arguments
    if [ "$#" -ne 1 ]; then
       echo "Usage: $0 <file_or_directory>"
       exit 1
    fi
    
    TARGET="$1"
    
    # Check if the target is a directory
    if [ -d "$TARGET" ]; then
       for file in "$TARGET"/*.hql; do
          [ -e "$file" ] || continue # Skip if no HQL files found
          process_file "$file"
       done
    elif [ -f "$TARGET" ]; then
       process_file "$TARGET"
    else
       echo "Invalid input: $TARGET is neither a file nor a directory."
       exit 1
    fi
    

    이 스크립트는 Apache Impala 스크립트를 Trino로 변환하기 위한 기본 접근 방식을 제공하지만 가능한 모든 Impala 명령문 및 시나리오는 다루지 않습니다. 이 스크립트에서 처리되지 않는 추가 Impala 특정 구문 또는 함수가 발생하는 경우 해당 사례를 처리하기 위해 새 변환 규칙을 추가하여 스크립트를 확장할 수 있습니다. 이러한 수정 사항으로 스크립트를 정기적으로 업데이트하면 이후 변환에 대한 효율성이 향상됩니다.

작업 2: 변환 스크립트 실행

  1. 스크립트를 Oracle Big Data Service 에지 노드에 저장합니다.

  2. Apache Impala 스크립트를 포함하는 대상 파일 또는 디렉토리로 스크립트를 실행합니다.

  3. 생성된 출력 및 차이 파일에서 변경 사항을 검토합니다.

태스크 3: 수동 조정

  1. LEFT ANTI JOIN 문을 사용합니다.

    LEFT ANTI JOIN와 같은 특정 Apache Impala SQL 생성자는 수동 개입이 필요합니다. Impala 스크립트에서 LEFT ANTI JOIN 문을 식별하고 LEFT JOINWHERE 절로 사용하여 동등한 Trino SQL로 바꿉니다.

    스크립트 예:

    • Impala 스크립트:

      SELECT t1.*
       FROM table1 t1
       LEFT ANTI JOIN table2 t2 ON t1.key = t2.key;
      
    • Trino 스크립트:

      SELECT t1.*
       FROM table1 t1
       LEFT JOIN table2 t2 ON t1.key = t2.key
       WHERE t2.key IS NULL;
      
  2. GROUP BY 절을 조정합니다.

    Trino는 GROUP BY 절에서 위치 기반 그룹화를 허용하여 긴 그룹화를 단순화할 수 있습니다. 필요한 경우 GROUP BY 절에서 열 이름을 해당하는 위치로 변환합니다. 모든 열이 올바르게 매핑되었는지 확인하십시오.

    스크립트 예:

    • Impala 스크립트:

      GROUP BY sales_data.region,
          sales_data.product_name,
          sales_data.transaction_id,
          transaction_date,
          last_updated,
          inventory.stock_quantity,
          sales_amount,
          discount_status,
          inventory_cost,
          total_revenue,
          revenue_status,
          reporting_period,
          report_generated_date;
      
    • Trino 스크립트:

      GROUP BY sales_data.region,
          sales_data.product_name,
          sales_data.transaction_id,
          transaction_date,
          last_updated,
          inventory.stock_quantity,
          7,  -- sales_amount
          8,  -- discount_status
          9,  -- inventory_cost
          10, -- total_revenue
          11, -- revenue_status
          12, -- reporting_period
          13; -- report_generated_date
      

문제 해결 및 팁

다음 단계

Oracle Big Data Service에서 Apache Impala 스크립트를 Trino로 마이그레이션한 후에는 변환된 스크립트를 철저히 검증해야 합니다. 먼저 테스트 환경에서 변환된 Trino 스크립트를 실행하여 예상 결과를 생성합니다. Trino의 실행 엔진이 Impala에 비해 다르게 동작할 수 있으므로 특히 조인 또는 집계와 관련된 복잡한 질의에서 성능 차이에 주의하십시오.

또한 쿼리 계획을 분석하고 필요한 조정을 수행하여 새로운 Trino 쿼리를 최적화하는 것을 고려하십시오. 마지막으로, 마이그레이션 프로세스 중에 배운 특정 과제나 교훈을 문서화하십시오. 향후 마이그레이션에 유용할 것입니다.

감사의 글

추가 학습 자원

docs.oracle.com/learn에서 다른 실습을 탐색하거나 Oracle Learning YouTube 채널에서 더 많은 무료 학습 콘텐츠에 액세스하세요. 또한 Oracle Learning Explorer가 되려면 education.oracle.com/learning-explorer을 방문하십시오.

제품 설명서는 Oracle Help Center를 참조하십시오.