附註:

將 Apache Impala 命令檔移轉至 Oracle Big Data Service 上的 Trino

簡介

Oracle Big Data Service (BDS) 是由 Oracle 提供的雲端服務,可讓使用者建立與管理 Hadoop 叢集、Spark 叢集、Trino 及其他大數據服務。在大數據分析的世界中,Trino 扮演高效能分散式 SQL 查詢引擎,專為在大型資料集上執行互動式分析查詢所設計。Trino 可在統一 SQL 介面內跨多個來源查詢資料,包括 Hadoop 分散式檔案系統 (HDFS)、OCI 儲存桶及傳統關聯式資料庫。它能夠以低延遲處理大規模資料,使其成為資料工程師和分析師的強大工具。

隨著移轉至現代化資料平台,許多組織都在 Oracle Big Data Service 上從 Apache Impala 移轉至 Trino。本教學課程提供自動化和手動調整 Apache Impala 命令檔的逐步方法,讓您順暢地移轉至 Trino。

目標

必要條件

作業 1:自動化命令檔轉換

自動化指令碼旨在簡化將 Apache Impala 指令碼轉換成與 Oracle Big Data Service 上的 Trino 相容的程序。此指令碼可自動化數個關鍵轉換,減少所需的手動工作量,並將移轉過程中人為錯誤的可能性降到最低。以下是其功能的細目。

  1. 檔案處理與初始化。

    • 命令檔會從讀取輸入 .hql 檔案 (通常用於 Apache Impala 查詢) 開始。

    • 它會建立一個新的輸出檔案,其中會儲存轉換的 Trino 相容查詢。

    • 命令檔會從新增一行來設定目錄和資料庫相關資訊環境開始,使用 USE catalog_name.schema_name 來確保查詢在 Trino 的正確相關資訊環境中執行。

  2. 自動化查詢轉換。

    • 覆寫表格處理:命令檔會尋找 Apache Impala 命令檔中的任何 OVERWRITE TABLE 敘述句,並在它們上方插入對應的 DELETE FROM 敘述句。這可確保在插入新資料前清除表格,與 Trino 處理資料覆寫的方式一致。

    • 取代 Impala 特定函數:命令檔會套用數個轉換,將 Apache Impala 特定語法和函數取代為 Trino 相容的語法和函數。舉例而言:

      • 由於 Trino 中不需要 invalidate metadataREFRESH 指令,因此會對其加註。

      • 資料類型變更 (例如將 string 變更為 varchar) 以符合 Trino 的資料類型。

      • Impala 的 TRUNCATE TABLE 敘述句被 DELETE FROM 取代,因為 Trino 處理的截斷方式不同。

      • COMPUTE STATS 會由 ANALYZE 取代,以反映 Trino 如何收集統計資料。

      • 其他特定轉換包括處理 nvlifnullfrom_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 敘述句。

    某些 Apache Impala SQL 建構 (例如 LEFT ANTI JOIN) 需要手動介入。識別 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
      

疑難排解與秘訣

接下來的步驟

完成將 Apache Impala 命令檔移轉至 Oracle Big Data Service 上的 Trino 之後,請務必徹底驗證轉換後的命令檔。從在測試環境中執行轉換的 Trino 命令檔開始,以確保產生預期的結果。請密切注意效能差異,特別是在涉及結合或聚總的複雜查詢中,因為 Trino 的執行引擎可能與 Impala 不同。

此外,請考慮透過分析查詢計畫和進行必要的調整來最佳化新的 Trino 查詢。最後,記錄移轉過程中所學到的任何特定挑戰或課程,因為這對於未來的移轉至關重要。

致謝

其他學習資源

探索 docs.oracle.com/learn 上的其他實驗室,或存取 Oracle Learning YouTube 頻道上的更多免費學習內容。此外,請造訪 education.oracle.com/learning-explorer 以成為 Oracle Learning Explorer。

如需產品文件,請造訪 Oracle Help Center