OracleからMySQLへの移行:大量データロードとETLによる差分更新の実践

OracleからMySQLへのデータベース移行では、ストアドプロシージャやSQL構文の違いに加え、データ型の変換や文字コードの互換性が重要な課題となります。本記事では、大容量データの効率的な移行手法として、高速エクスポートツールとMySQLのロード機能を組み合わせた一括移行と、ETLツール「Kettle」を活用した差分更新の実装方法について解説します。

データ型のマッピングと注意点

移行に先立ち、OracleとMySQLのデータ型の違いを吸収する必要があります。
  1. 日付型の扱い OracleのDATE型は日付と時刻(年月日時分秒)を保持しますが、MySQLのDATE型は日付のみ(YYYY-MM-DD)を保持します。時刻情報が含まれる場合、MySQLではDATETIME型またはTIMESTAMP型にマッピングする必要があります。範囲と機能を考慮し、基本的にはDATETIME型の使用が推奨されます。デフォルト値としてSYSDATEを使用している場合、MySQLではDEFAULT CURRENT_TIMESTAMPとして定義します。
  2. 数値型の扱い OracleのNUMBER(p, 0)は精度が高く最大126桁まで扱えますが、MySQLのDECIMAL型は最大65桁です。桁数が大きい場合はDECIMAL(65, 0)とするか、範囲内であればBIGINT等への変換を検討します。
  3. 文字コード MySQL側の文字セットは、絵文字などにも対応できるようutf8mb4を推奨します。

大量データの移行:SQL*Loader類似ツールとLOAD DATAの活用

テラバイトクラスのデータを扱う場合、GUIツールによるエクスポートは性能限界に達しやすく、サーバーへの直接出力も困難です。そこで、コマンドラインベースの高速エクスポートツール(sqluldr2等)を用いてCSV形式でデータを抽出し、MySQLのLOAD DATA INFILE構文で取り込む手法が有効です。

1. 環境設定とエクスポート

移行元のOracleデータベースへ接続するため、クライアント環境を整えます。 tnsnames.oraの設定例:
ORACLE_SOURCE =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.100)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = orcl)
    )
  )
環境変数を設定した上で、エクスポートツールを実行します。データ内に改行や区切り文字が含まれる可能性があるため、区切り文字には特殊文字(例:16進数の0x07)を指定し、データの整合性を担保します。 エクスポート実行コマンド例:
./sqluldr2_linux64.bin USER=scott/tiger@ORACLE_SOURCE \
  QUERY="SELECT /*+ PARALLEL(4) */ * FROM large_transaction_table" \
  FIELD=0x07 \
  FILE=/data/export/transaction_data%B.csv \
  LOG=/data/export/export.log \
  SIZE=50MB \
  SAFE=YES
ここで、SIZEパラメータでファイルサイズを分割することで、大容量ファイルの取り扱いリスクを軽減できます。

2. MySQLへのインポート

MySQLへのデータロードはLOAD DATA構文を使用します。並列処理でエクスポートされた複数のファイルを順次取り込みます。
LOAD DATA INFILE '/data/export/transaction_data001.csv'
INTO TABLE target_db.transaction_table
CHARACTER SET utf8mb4
FIELDS TERMINATED BY X'07'
ENCLOSED BY '"'
LINES TERMINATED BY '\n';

ETLツール「Kettle」による差分更新

一括移行後、稼働中のシステムで発生した差分データを同期するために、オープンソースのETLツールであるKettle(Pentaho Data Integration)を利用します。KettleはJavaベースでクロスプラットフォーム対応であり、ビジュアルエディタで変換処理を設計できます。

差分更新ジョブの設計フロー

差分更新の核心は、新旧データソースを比較し、変更があったレコードのみを抽出・反映することです。
  1. データソースの入力 「表入力」ステップを2つ配置し、それぞれ現在の生産データ(新データ)と移行済みのターゲットデータ(旧データ)への接続を定義します。
  2. データの整列 「マージ(Merge Rows)」ステップでデータを比較するためには、両方のデータセットが比較キー(主キーなど)でソートされている必要があります。「ソート」ステップを事前に配置します。
  3. マージ(差分検出) 「マージ(差分)」ステップを使用して新旧データを結合します。このステップでは以下のフラグフィールドを出力するように設定します。
    • identical: 変更なし
    • changed: データの変更あり
    • new: 新規レコード
    • deleted: 削除されたレコード
  4. 処理の分岐 「Switch / Case」ステップを利用し、フラグの値に応じて後続の処理を分岐させます。
    • new → INSERT処理
    • changed → UPDATE処理
    • deleted → DELETE処理(または論理削除)

ジョブのスケジューリング

作成した変換ファイルを定期実行するために、ジョブファイルを作成し、OSのスケジューラから呼び出します。 Linux環境での実行スクリプト例:
#!/bin/bash
export JAVA_HOME=/usr/lib/jvm/java-11
cd /opt/kettle/data-integration

./kitchen.sh \
  -file=/opt/kettle/jobs/incremental_sync.kjb \
  -level=Basic \
  -log=/var/log/kettle/sync.log

MySQLにおけるコメントの一括付与

移行後のテーブル定義にコメントを付与する場合、メタデータテーブルを利用してALTER文を生成できます。
SELECT
    CONCAT(
        'ALTER TABLE ', table_schema, '.', table_name,
        ' MODIFY COLUMN ', column_name, ' ', column_type,
        IF(is_nullable = 'NO', ' NOT NULL', ''),
        IF(column_default IS NOT NULL, CONCAT(' DEFAULT ', QUOTE(column_default)), ''),
        ' COMMENT ', QUOTE(column_comment), ';'
    ) AS alter_statement
FROM information_schema.columns
WHERE table_schema = 'target_db_name';

トラブルシューティング:空文字の扱い

Kettleはデフォルト設定で空文字列をNULLとして扱う傾向があります。これにより、NOT NULL制約のあるカラムへ空文字を挿入しようとしてエラーが発生することがあります。これを防ぐには、Kettleの設定ファイル(kettle.properties)に以下の行を追加します。
KETTLE_EMPTY_STRING_DIFFERS_FROM_NULL=Y
この設定により、空文字列とNULLが明示的に区別され、データ整合性の問題を回避できます。

タグ: Oracle MySQL データ移行 ETL Kettle

5月20日 20:36 投稿