LoginSignup
1
1

More than 5 years have passed since last update.

RedshiftからRDSにコピーする

Last updated at Posted at 2016-09-08

概要

大量のデータをコピーしたい場合、SELECTして成形INSERTする作業をコード上で行おうとすると、メモリを食うしバッチサーバの性能が悪いとCPUネックで遅くなる

AWS上で出来るだけ早くうまいことしたい

手順

今回は以下のようにやってみた。

  • Redshift → S3 に UNLOAD
  • S3 → Batch Server にファイルをダウンロード
  • Batch Server → RDS に LOAD DATA LOCAL INFILE

Redshift → S3

LOAD DATA LOCAL INFILE に流せるように CSV 形式で UNLOAD する
通常 UNLOAD は、ノードスライス単位で並列に行うので SELECT することを考えるとだいぶ速い

UNLOAD ( ${SELECT_STATEMENT} )
TO 's3:// ${S3_PATH} '
CREDENTIALS 'aws_access_key_id= ${ACCESS_KEY_ID} ;aws_secret_access_key= ${ACCESS_KEY} '
DELIMITER ','
ALLOWOVERWRITE
ESCAPE
ADDQUOTES

S3 → Batch Server → RDS

# ruby

bucket = AWS::S3.new.buckets[BUCKET_NAME]
tree = bucket.as_tree({prefix: S3_DIR_PATH})

# UNLOADしたファイルリスト
file_paths = tree.children.select(&:leaf?).map(&:key).reject{|f| f.match(/\/\z/)}
file_paths.each do |file_path|
  local_file_path = "/tmp/#{file_path.gsub('/', '_')}_#{Process.pid}"

  begin
    # UNLOADしたファイルをBatch Serverに書き込む
    File.open(local_file_path, 'w') do |f|
      bucket.objects[file_path].read do |chunk|
        f.write(chunk.force_encoding("UTF-8"))
      end
    end

    # utf8mb4で読み込む
    ActiveRecord::Base.connection.execute("SET character_set_database=utf8mb4")

    # RDSにImport
    ActiveRecord::Base.connection.execute(<<-SQL
      LOAD DATA LOCAL INFILE '#{local_file_path}'
        REPLACE INTO TABLE #{TABLE_NAME}
        FIELDS TERMINATED BY ',' ENCLOSED BY '"'
    SQL
    )
  ensure
    # ファイル削除
    File.unlink local_file_path
  end
end

追記: ESCAPEがうまくいかない

UNLOAD時に、不適切なところでESCAPEされ、RDSにLOADするときにエラーが起きる場合があった。
今回の場合、以下のようにtextカラムの最後の文字が(だった場合におかしくなった。

# expected
"hoge","huga","longtext (","2016-01-01"

# actual
"hoge","huga","longtext (\,"2016-01-01"

対処法あれば教えて下さい。。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1