ノート:

Oracle Big Data ServiceでのApache ImpalaスクリプトのTrinoへの移行

イントロダクション

Oracle Big Data Service (BDS)は、Oracleが提供するクラウドベースのサービスで、ユーザーはHadoopクラスタ、Sparkクラスタ、Trinoおよびその他のビッグ・データ・サービスを作成および管理できます。ビッグ・データ分析の世界では、Trinoは、大規模なデータセットに対してインタラクティブな分析クエリを実行するために設計された、高パフォーマンスの分散SQLクエリエンジンとして際立っています。Trinoは、Hadoop Distributed File System (HDFS)、OCIバケット、従来のリレーショナル・データベースなど、複数のソースにわたるデータの問合せをすべて統合SQLインタフェース内で可能にします。大規模なデータを低レイテンシで処理できるため、データ・エンジニアやアナリストにとっても強力なツールとなります。

最新のデータ・プラットフォームへの移行に伴い、多くの組織がApache ImpalaからOracle Big Data ServiceのTrinoに移行しています。このチュートリアルでは、Trinoへのシームレスな移行のためにApache Impalaスクリプトを自動化および手動で調整するためのステップバイステップのアプローチを提供します。

目的

前提条件

タスク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互換の構文および関数に置き換えます。たとえば次のようにします。

      • invalidate metadataおよびREFRESHコマンドは、Trinoでは必要ないためコメント・アウトされます。

      • stringからvarcharへのデータ型の変更は、Trinoのデータ型と一致するように行われます。

      • Trinoは切り捨てを異なる方法で処理するため、ImpalaのTRUNCATE TABLE文はDELETE FROMに置き換えられます。

      • COMPUTE STATSは、Trinoが統計を収集する方法を反映してANALYZEに置き換えられます。

      • その他の特定の変換には、nvlifnullfrom_utc_timestampなどの関数の処理が含まれ、Trinoの構文および関数に調整されます。

      • nvl2関数を同等のCOALESCE式に変換します。

      • 正規表現の一致(REGEXP)をTrinoのREGEXP_LIKEに変更しています。

      • Trinoの構文に合わせて日付関数とタイムスタンプ関数を調整します。

    スクリプト:

    #!/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;
      
    • トリノ・スクリプト:

      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;
      
    • トリノ・スクリプト:

      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チャネルで無料のラーニング・コンテンツにアクセスしてください。また、education.oracle.com/learning-explorerにアクセスしてOracle Learning Explorerになります。

製品ドキュメントは、Oracle Help Centerを参照してください。